diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index c01d6d9..b49e513 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,7 +1,6 @@
[bumpversion]
-current_version = 1.0.6
+current_version = 2.0.0
commit = True
message = Bumps version to {new_version}
tag = False
tag_name = {new_version}
-
diff --git a/.gitignore b/.gitignore
index 83a6501..1ee06b4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,7 @@ tardigrade-ci/
# eclint
.git/
+
+# terratest
+go.mod
+go.sum
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bf5c593..95f293b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,24 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).
+### 2.0.0
+
+**Released**: 2020.09.15
+
+**Commit Delta**: [Change from 1.0.4 release](https://github.com/plus3it/terraform-aws-tardigrade-config-rules/compare/1.0.4..2.0.0)
+
+**Summary**:
+
+* Entirely reworks module to be unopionated and allow users to create arbitrary config rules.
+ See `tests/create_legacy_config_rules` for a configuration that uses the new module to create
+ the prior set of config rules.
+* Removes the var `create_config_rules`. Instead, use tf 0.13 and `count`/`for_each` on the module
+ block. See `tests/no_create`.
+* Removes vendored custom config_rules, and instead uses a module block to pull them down during
+ `terraform init`. As a result, the `source_path` for custom config_rules has changed. For an example,
+ see the `source_path` argument in `tests/create_custom_config_rule`.
+* Outputs the Config Rule object as `config_rule`.
+
### 1.0.4
**Released**: 2019.10.28
diff --git a/Makefile b/Makefile
index f5e466e..9654deb 100644
--- a/Makefile
+++ b/Makefile
@@ -1,14 +1,3 @@
export FIND_EXCLUDES = -not \( -name .terraform -prune \) -not \( -name .terragrunt-cache -prune \) -not \( -name vendor -prune \)
-include $(shell curl -sSL -o .tardigrade-ci "https://raw.githubusercontent.com/plus3it/tardigrade-ci/master/bootstrap/Makefile.bootstrap"; echo .tardigrade-ci)
-
-clean::
- rm -rf vendor
-
-VENDOR ?= vendor/github.com/awslabs/aws-config-rules
-vendor: $(VENDOR)
- echo "root = true" > vendor/.editorconfig
-
-vendor/%:
- git clone https://$(*).git vendor/$*
- rm -rf vendor/$*/.git
diff --git a/README.md b/README.md
index d7289d5..eea6f65 100644
--- a/README.md
+++ b/README.md
@@ -14,30 +14,26 @@ make clean && make vendor
| Name | Version |
|------|---------|
-| terraform | >= 0.12 |
+| terraform | >= 0.13 |
## Providers
| Name | Version |
|------|---------|
| aws | n/a |
-| null | n/a |
-| template | n/a |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
-| cloudtrail\_bucket | Name of S3 bucket to validate that CloudTrail logs are being delivered | `string` | `null` | no |
-| config\_bucket | Name of S3 bucket to validate that Config is configured to send inventory to | `string` | `null` | no |
-| config\_recorder | The name of the AWS Config recorder | `string` | `null` | no |
-| config\_sns\_topic\_arn | ARN of SNS topic to validate that Config changes are being streamed to | `string` | `null` | no |
-| create\_config\_rules | Controls whether to create the AWS Config Rules | `bool` | `true` | no |
-| exclude\_rules | List of config rule resource names to exclude from creation | `list(string)` | `[]` | no |
-| tags | Map of tags to apply to the resources | `map(string)` | `{}` | no |
+| config\_recorder\_id | ID of the config recorder in the account. Required to address the implicit dependency on the config recorder | `string` | n/a | yes |
+| config\_rule | Object of attributes for the config rule resource, see https://www.terraform.io/docs/providers/aws/r/config_config_rule.html#argument-reference. When `owner` is `AWS`, set `source_identifer` to the AWS predefined identifier for the rule. When `owner` is `CUSTOM_LAMBDA`, set `source_identifier` to `null` and it will be set to the ARN of the lambda function |
object({ description = string input_parameters = string maximum_execution_frequency = string name = string owner = string source_identifier = string tags = map(string) scope = object({ compliance_resource_id = string compliance_resource_types = list(string) tag_key = string tag_value = string }) source_details = list(object({ message_type = string })) }) | n/a | yes |
+| lambda | Object of attributes for the lambda supporting a custom config rule, see https://www.terraform.io/docs/providers/aws/r/lambda_function.html#argument-reference. Required when `config_rule.owner` is `CUSTOM_LAMBDA` | object({ description = string handler = string name = string policy = string runtime = string source_path = string reserved_concurrent_executions = number tags = map(string) timeout = number }) | { "description": null, "handler": null, "name": null, "policy": null, "reserved_concurrent_executions": null, "runtime": null, "source_path": null, "tags": null, "timeout": null } | no |
## Outputs
-No output.
+| Name | Description |
+|------|-------------|
+| config\_rule | AWS Config Rule object |
diff --git a/main.tf b/main.tf
index da70e47..ade1226 100644
--- a/main.tf
+++ b/main.tf
@@ -1,624 +1,91 @@
-provider "aws" {
-}
-
-locals {
- exclude_cloudtrail_enabled = "${contains(var.exclude_rules, "cloudtrail_enabled")}"
- exclude_iam_password_policy = "${contains(var.exclude_rules, "iam_password_policy")}"
- exclude_s3_bucket_public_read_prohibited = "${contains(var.exclude_rules, "s3_bucket_public_read_prohibited")}"
- exclude_s3_bucket_public_write_prohibited = "${contains(var.exclude_rules, "s3_bucket_public_write_prohibited")}"
- exclude_s3_bucket_ssl_requests_only = "${contains(var.exclude_rules, "s3_bucket_ssl_requests_only")}"
- exclude_codebuild_project_envvar_awscred_check = "${contains(var.exclude_rules, "codebuild_project_envvar_awscred_check")}"
- exclude_codebuild_project_source_repo_url_check = "${contains(var.exclude_rules, "codebuild_project_source_repo_url_check")}"
- exclude_instances_in_vpc = "${contains(var.exclude_rules, "instances_in_vpc")}"
- exclude_ec2_volume_inuse_check = "${contains(var.exclude_rules, "ec2_volume_inuse_check")}"
- exclude_eip_attached = "${contains(var.exclude_rules, "eip_attached")}"
- exclude_lambda_function_public_access_prohibited = "${contains(var.exclude_rules, "lambda_function_public_access_prohibited")}"
- exclude_root_account_mfa_enabled = "${contains(var.exclude_rules, "root_account_mfa_enabled")}"
- exclude_iam_access_key_rotation_check = "${contains(var.exclude_rules, "iam_access_key_rotation_check")}"
- exclude_rds_vpc_public_subnet = "${contains(var.exclude_rules, "rds_vpc_public_subnet")}"
- exclude_iam_user_active = "${contains(var.exclude_rules, "iam_user_active")}"
- exclude_config_enabled = "${contains(var.exclude_rules, "config_enabled")}"
- exclude_iam_mfa_for_console_access = "${contains(var.exclude_rules, "iam_mfa_for_console_access")}"
- exclude_restricted_common_ports_access = "${contains(var.exclude_rules, "restricted_common_ports_access")}"
- exclude_restricted_common_ports_database = "${contains(var.exclude_rules, "restricted_common_ports_database")}"
- exclude_ebs_snapshot_public_restorable_check = "${contains(var.exclude_rules, "ebs_snapshot_public_restorable_check")}"
-}
-
-locals {
- aws_config_rules = "${path.module}/vendor/github.com/awslabs/aws-config-rules"
-}
-
-data "aws_partition" "current" {
- count = var.create_config_rules ? 1 : 0
-}
-
-data "aws_caller_identity" "this" {
- count = var.create_config_rules ? 1 : 0
-}
-
-data "template_file" "cloudtrail_enabled" {
- count = var.create_config_rules ? 1 : 0
-
- template = <<-EOF
- {"s3BucketName":"$${cloudtrail_bucket}"}
- EOF
-
-
- vars = {
- cloudtrail_bucket = var.cloudtrail_bucket
- }
-}
-
-data "template_file" "iam_password_policy" {
- count = var.create_config_rules ? 1 : 0
-
- template = <<-EOF
- {
- "RequireUppercaseCharacters":"true",
- "RequireLowercaseCharacters":"true",
- "RequireSymbols":"true",
- "RequireNumbers":"true",
- "MinimumPasswordLength":"14",
- "PasswordReusePrevention":"24",
- "MaxPasswordAge":"60"
- }
- EOF
-
-}
-
-resource "null_resource" "dependencies" {
- count = var.create_config_rules ? 1 : 0
-
- triggers = {
- config_recorder = var.config_recorder
- }
-}
-
-resource "aws_config_config_rule" "cloudtrail_enabled" {
- count = var.create_config_rules && ! local.exclude_cloudtrail_enabled ? 1 : 0
-
- name = "cloudtrail-enabled"
- description = "Checks whether AWS CloudTrail is enabled in your AWS account. Optionally, you can specify which S3 bucket, SNS topic, and Amazon CloudWatch Logs ARN to use"
- input_parameters = data.template_file.cloudtrail_enabled[0].rendered
-
- source {
- owner = "AWS"
- source_identifier = "CLOUD_TRAIL_ENABLED"
- }
-
- depends_on = [null_resource.dependencies]
-}
-
-resource "aws_config_config_rule" "iam_password_policy" {
- count = var.create_config_rules && ! local.exclude_iam_password_policy ? 1 : 0
-
- name = "iam-password-policy"
- description = "Checks whether the account password policy for IAM users meets the specified requirements"
- input_parameters = data.template_file.iam_password_policy[0].rendered
-
- source {
- owner = "AWS"
- source_identifier = "IAM_PASSWORD_POLICY"
- }
-
- depends_on = [null_resource.dependencies]
-}
-
-resource "aws_config_config_rule" "s3_bucket_public_read_prohibited" {
- count = var.create_config_rules && ! local.exclude_s3_bucket_public_read_prohibited ? 1 : 0
-
- name = "s3-bucket-public-read-prohibited"
- description = "Checks that your Amazon S3 buckets do not allow public read access. The rule checks the Block Public Access settings, the bucket policy, and the bucket access control list (ACL)"
-
- source {
- owner = "AWS"
- source_identifier = "S3_BUCKET_PUBLIC_READ_PROHIBITED"
- }
-
- depends_on = [null_resource.dependencies]
-}
-
-resource "aws_config_config_rule" "s3_bucket_public_write_prohibited" {
- count = var.create_config_rules && ! local.exclude_s3_bucket_public_write_prohibited ? 1 : 0
-
- name = "s3-bucket-public-write-prohibited"
- description = "Checks that your Amazon S3 buckets do not allow public write access. The rule checks the Block Public Access settings, the bucket policy, and the bucket access control list (ACL)"
-
- source {
- owner = "AWS"
- source_identifier = "S3_BUCKET_PUBLIC_WRITE_PROHIBITED"
- }
-
- depends_on = [null_resource.dependencies]
-}
-
-resource "aws_config_config_rule" "s3_bucket_ssl_requests_only" {
- count = var.create_config_rules && ! local.exclude_s3_bucket_ssl_requests_only ? 1 : 0
-
- name = "s3-bucket-ssl-requests-only"
- description = "Checks whether S3 buckets have policies that require requests to use Secure Socket Layer (SSL)"
-
- source {
- owner = "AWS"
- source_identifier = "S3_BUCKET_SSL_REQUESTS_ONLY"
- }
-
- depends_on = [null_resource.dependencies]
-}
-
-resource "aws_config_config_rule" "codebuild_project_envvar_awscred_check" {
- count = var.create_config_rules && ! local.exclude_codebuild_project_envvar_awscred_check ? 1 : 0
-
- name = "codebuild-project-envvar-awscred-check"
- description = "Checks whether the project contains environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. The rule is NON_COMPLIANT when the project environment variables contains plaintext credentials"
-
- source {
- owner = "AWS"
- source_identifier = "CODEBUILD_PROJECT_ENVVAR_AWSCRED_CHECK"
- }
-
- depends_on = [null_resource.dependencies]
-}
-
-resource "aws_config_config_rule" "codebuild_project_source_repo_url_check" {
- count = var.create_config_rules && ! local.exclude_codebuild_project_source_repo_url_check ? 1 : 0
-
- name = "codebuild-project-source-repo-url-check"
- description = "Checks whether the GitHub or Bitbucket source repository URL contains either personal access tokens or user name and password. The rule is COMPLIANT with the usage of OAuth to grant authorization for accessing GitHub or Bitbucket repositories"
-
- source {
- owner = "AWS"
- source_identifier = "CODEBUILD_PROJECT_SOURCE_REPO_URL_CHECK"
- }
-
- depends_on = [null_resource.dependencies]
-}
-
-resource "aws_config_config_rule" "instances_in_vpc" {
- count = var.create_config_rules && ! local.exclude_instances_in_vpc ? 1 : 0
-
- name = "instances-in-vpc"
- description = "Checks whether your EC2 instances belong to a virtual private cloud (VPC). Optionally, you can specify the VPC ID to associate with your instances"
-
- source {
- owner = "AWS"
- source_identifier = "INSTANCES_IN_VPC"
- }
-
- depends_on = [null_resource.dependencies]
-}
-
-resource "aws_config_config_rule" "ec2_volume_inuse_check" {
- count = var.create_config_rules && ! local.exclude_ec2_volume_inuse_check ? 1 : 0
-
- name = "ec2-volume-inuse-check"
- description = "Checks whether EBS volumes are attached to EC2 instances. Optionally checks if EBS volumes are marked for deletion when an instance is terminated"
-
- source {
- owner = "AWS"
- source_identifier = "EC2_VOLUME_INUSE_CHECK"
- }
-
- depends_on = [null_resource.dependencies]
-}
-
-resource "aws_config_config_rule" "eip_attached" {
- count = var.create_config_rules && ! local.exclude_eip_attached ? 1 : 0
-
- name = "eip-attached"
- description = "Checks whether all Elastic IP addresses that are allocated to a VPC are attached to EC2 instances or in-use elastic network interfaces (ENIs)"
-
- source {
- owner = "AWS"
- source_identifier = "EIP_ATTACHED"
- }
-
- depends_on = [null_resource.dependencies]
-}
-
-resource "aws_config_config_rule" "lambda_function_public_access_prohibited" {
- count = var.create_config_rules && ! local.exclude_lambda_function_public_access_prohibited ? 1 : 0
-
- name = "lambda-function-public-access-prohibited"
- description = "Checks whether the AWS Lambda function policy attached to the Lambda resource prohibits public access. If the Lambda function policy allows public access it is NON_COMPLIANT"
-
- source {
- owner = "AWS"
- source_identifier = "LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED"
- }
-
- depends_on = [null_resource.dependencies]
-}
-
-resource "aws_config_config_rule" "root_account_mfa_enabled" {
- count = var.create_config_rules && ! local.exclude_root_account_mfa_enabled ? 1 : 0
-
- name = "root-account-mfa-enabled"
- description = "Checks whether users of your AWS account require a multi-factor authentication (MFA) device to sign in with root credentials"
-
- source {
- owner = "AWS"
- source_identifier = "ROOT_ACCOUNT_MFA_ENABLED"
- }
-
- depends_on = [null_resource.dependencies]
-}
-
-resource "aws_config_config_rule" "iam_user_active" {
- count = var.create_config_rules && ! local.exclude_iam_user_active ? 1 : 0
-
- name = "iam-user-active"
- description = "Checks whether your AWS Identity and Access Management (IAM) users have passwords or active access keys that have not been used within the specified number of days you provided"
-
- input_parameters = <<-EOF
- {
- "maxCredentialUsageAge":"90"
- }
- EOF
-
- source {
- owner = "AWS"
- source_identifier = "IAM_USER_UNUSED_CREDENTIALS_CHECK"
- }
-
- depends_on = [null_resource.dependencies]
-}
-
-###########################
-### CUSTOM CONFIG RULES ###
-###########################
-data "aws_iam_policy" "config_rules" {
- count = var.create_config_rules ? 1 : 0
-
- arn = "arn:${data.aws_partition.current[0].partition}:iam::aws:policy/service-role/AWSConfigRulesExecutionRole"
-}
-
-### iam_access_key_rotation_check
-data "aws_iam_policy_document" "lambda_iam_access_key_rotation_check" {
- count = var.create_config_rules && ! local.exclude_iam_access_key_rotation_check ? 1 : 0
-
- source_json = data.aws_iam_policy.config_rules[0].policy
-
- statement {
- actions = ["iam:ListAccessKeys"]
- resources = ["*"]
- }
-}
-
-module "lambda_iam_access_key_rotation_check" {
- source = "git::https://github.com/plus3it/terraform-aws-lambda.git?ref=v1.2.0"
-
- function_name = "config_rule_iam_access_key_rotation_check"
- description = "Checks that IAM User Access Keys have been rotated within the specified number of days"
- handler = "iam_access_key_rotation-triggered.handler"
- runtime = "nodejs10.x"
- timeout = 15
- tags = var.tags
-
- reserved_concurrent_executions = "-1"
-
- source_path = "${local.aws_config_rules}/node/iam_access_key_rotation-triggered.js"
-
- policy = var.create_config_rules ? data.aws_iam_policy_document.lambda_iam_access_key_rotation_check[0] : null
-}
-
-resource "aws_lambda_permission" "iam_access_key_rotation_check" {
- count = var.create_config_rules && ! local.exclude_iam_access_key_rotation_check ? 1 : 0
-
- action = "lambda:InvokeFunction"
- function_name = module.lambda_iam_access_key_rotation_check.function_name
- principal = "config.amazonaws.com"
- source_account = data.aws_caller_identity.this[0].account_id
-}
-
-resource "aws_config_config_rule" "iam_access_key_rotation_check" {
- count = var.create_config_rules && ! local.exclude_iam_access_key_rotation_check ? 1 : 0
-
- name = "iam-access-key-rotation-check"
- description = "Checks that IAM User Access Keys have been rotated within the specified number of days"
-
- input_parameters = <<-EOF
- {
- "MaximumAccessKeyAge": "90"
- }
- EOF
-
-
- scope {
- compliance_resource_types = ["AWS::IAM::User"]
- }
+resource "aws_config_config_rule" "this" {
+ name = var.config_rule.name
+ description = var.config_rule.description
+ input_parameters = var.config_rule.input_parameters
+ maximum_execution_frequency = var.config_rule.maximum_execution_frequency
+ tags = var.config_rule.tags
source {
- owner = "CUSTOM_LAMBDA"
- source_identifier = module.lambda_iam_access_key_rotation_check.function_arn
+ owner = var.config_rule.owner
+ source_identifier = local.custom_lambda ? module.custom_lambda[0].function_arn : var.config_rule.source_identifier
- source_detail {
- message_type = "ConfigurationItemChangeNotification"
- }
+ dynamic source_detail {
+ for_each = var.config_rule.source_details != null ? var.config_rule.source_details : []
- source_detail {
- message_type = "OversizedConfigurationItemChangeNotification"
+ content {
+ message_type = source_detail.value.message_type
+ }
}
}
- depends_on = [
- aws_lambda_permission.iam_access_key_rotation_check,
- null_resource.dependencies,
- ]
-}
-
-### rds_vpc_public_subnet
-data "aws_iam_policy_document" "lambda_rds_vpc_public_subnet" {
- count = var.create_config_rules && ! local.exclude_rds_vpc_public_subnet ? 1 : 0
-
- source_json = data.aws_iam_policy.config_rules[0].policy
-
- statement {
- actions = ["ec2:DescribeRouteTables"]
- resources = ["*"]
- }
-}
+ dynamic scope {
+ for_each = var.config_rule.scope != null ? [var.config_rule.scope] : []
-module "lambda_rds_vpc_public_subnet" {
- source = "git::https://github.com/plus3it/terraform-aws-lambda.git?ref=v1.2.0"
-
- function_name = "config_rule_rds_vpc_public_subnet"
- description = "Checks that no RDS Instances are in a Public Subnet"
- handler = "rds_vpc_public_subnet.lambda_handler"
- runtime = "python3.6"
- timeout = 15
- tags = var.tags
-
- reserved_concurrent_executions = "-1"
-
- source_path = "${local.aws_config_rules}/python/rds_vpc_public_subnet.py"
-
- policy = var.create_config_rules ? data.aws_iam_policy_document.lambda_iam_access_key_rotation_check[0] : null
-}
-
-resource "aws_lambda_permission" "rds_vpc_public_subnet" {
- count = var.create_config_rules && ! local.exclude_rds_vpc_public_subnet ? 1 : 0
-
- action = "lambda:InvokeFunction"
- function_name = module.lambda_rds_vpc_public_subnet.function_name
- principal = "config.amazonaws.com"
- source_account = data.aws_caller_identity.this[0].account_id
-}
-
-resource "aws_config_config_rule" "rds_vpc_public_subnet" {
- count = var.create_config_rules && ! local.exclude_rds_vpc_public_subnet ? 1 : 0
-
- name = "rds-vpc-public-subnet"
- description = "Checks that no RDS Instances are in a Public Subnet"
-
- scope {
- compliance_resource_types = ["AWS::RDS::DBInstance"]
- }
-
- source {
- owner = "CUSTOM_LAMBDA"
- source_identifier = module.lambda_rds_vpc_public_subnet.function_arn
-
- source_detail {
- message_type = "ConfigurationItemChangeNotification"
- }
-
- source_detail {
- message_type = "OversizedConfigurationItemChangeNotification"
+ content {
+ compliance_resource_id = scope.value.compliance_resource_id
+ compliance_resource_types = scope.value.compliance_resource_types
+ tag_key = scope.value.tag_key
+ tag_value = scope.value.tag_value
}
}
depends_on = [
- aws_lambda_permission.rds_vpc_public_subnet,
- null_resource.dependencies,
+ aws_lambda_permission.custom_lambda,
+ var.config_recorder_id,
]
}
-### config_enabled
-data "aws_iam_policy_document" "lambda_config_enabled" {
- count = "${var.create_config_rules && ! local.exclude_config_enabled ? 1 : 0}"
-
- source_json = "${data.aws_iam_policy.config_rules[0].policy}"
+module "vendor" {
+ count = local.custom_lambda ? 1 : 0
+ source = "git::https://github.com/plus3it/aws-config-rules.git?ref=e6fe305462333b26b55b30fc8586c4cf6f853907"
}
-module "lambda_config_enabled" {
+module "custom_lambda" {
+ count = local.custom_lambda ? 1 : 0
source = "git::https://github.com/plus3it/terraform-aws-lambda.git?ref=v1.2.0"
- function_name = "config_rule_config_enabled"
- description = "Checks that Config has been activated and is logging to a specific bucket and sending to a specifc SNS topic"
- handler = "config_enabled.lambda_handler"
- runtime = "python3.6"
- timeout = 15
- tags = var.tags
-
- reserved_concurrent_executions = "-1"
-
- source_path = "${local.aws_config_rules}/python/config_enabled.py"
+ function_name = var.lambda.name
+ description = var.lambda.description
+ handler = var.lambda.handler
+ policy = data.aws_iam_policy_document.custom_lambda[0]
+ runtime = var.lambda.runtime
+ source_path = var.lambda.source_path
+ timeout = var.lambda.timeout
+ tags = var.lambda.tags
- policy = var.create_config_rules ? data.aws_iam_policy_document.lambda_config_enabled[0] : null
+ reserved_concurrent_executions = var.lambda.reserved_concurrent_executions
}
-resource "aws_lambda_permission" "config_enabled" {
- count = var.create_config_rules && ! local.exclude_config_enabled ? 1 : 0
+resource "aws_lambda_permission" "custom_lambda" {
+ count = local.custom_lambda ? 1 : 0
action = "lambda:InvokeFunction"
- function_name = module.lambda_config_enabled.function_name
+ function_name = module.custom_lambda[0].function_name
principal = "config.amazonaws.com"
source_account = data.aws_caller_identity.this[0].account_id
}
-resource "aws_config_config_rule" "config_enabled" {
- count = var.create_config_rules && ! local.exclude_config_enabled ? 1 : 0
-
- name = "config-enabled"
- description = "Checks that Config has been activated and is logging to a specific bucket and sending to a specifc SNS topic"
-
- input_parameters = <<-EOF
- {
- "s3BucketName": "${var.config_bucket}",
- "snsTopicARN": "${var.config_sns_topic_arn}"
- }
- EOF
-
-
- scope {
- compliance_resource_types = ["AWS::IAM::User"]
- }
-
- source {
- owner = "CUSTOM_LAMBDA"
- source_identifier = module.lambda_config_enabled.function_arn
-
- source_detail {
- message_type = "ScheduledNotification"
- }
- }
-
- depends_on = [
- aws_lambda_permission.config_enabled,
- null_resource.dependencies,
- ]
-}
-
-### iam_mfa_for_console_access
-data "aws_iam_policy_document" "lambda_iam_mfa_for_console_access" {
- count = var.create_config_rules && ! local.exclude_iam_mfa_for_console_access ? 1 : 0
-
- source_json = data.aws_iam_policy.config_rules[0].policy
-
- statement {
- actions = [
- "iam:ListMFADevices",
- "iam:GetLoginProfile",
- ]
-
- resources = ["*"]
- }
-}
-
-module "lambda_iam_mfa_for_console_access" {
- source = "git::https://github.com/plus3it/terraform-aws-lambda.git?ref=v1.2.0"
-
- function_name = "config_rule_iam_mfa_for_console_access"
- description = "Checks that all IAM users with console access have at least one MFA device"
- handler = "iam_mfa_for_console_access.lambda_handler"
- runtime = "python3.6"
- timeout = 15
- tags = var.tags
-
- reserved_concurrent_executions = "-1"
-
- source_path = "${local.aws_config_rules}/python/iam_mfa_for_console_access.py"
-
- policy = var.create_config_rules ? data.aws_iam_policy_document.lambda_iam_mfa_for_console_access[0] : null
-}
-
-resource "aws_lambda_permission" "iam_mfa_for_console_access" {
- count = var.create_config_rules && ! local.exclude_iam_mfa_for_console_access ? 1 : 0
-
- action = "lambda:InvokeFunction"
- function_name = module.lambda_iam_mfa_for_console_access.function_name
- principal = "config.amazonaws.com"
- source_account = data.aws_caller_identity.this[0].account_id
+data "aws_caller_identity" "this" {
+ count = local.custom_lambda ? 1 : 0
}
-resource "aws_config_config_rule" "iam_mfa_for_console_access" {
- count = var.create_config_rules && ! local.exclude_iam_mfa_for_console_access ? 1 : 0
-
- name = "iam-mfa-for-console-access"
- description = "Checks that all IAM users with console access have at least one MFA device"
-
- scope {
- compliance_resource_types = ["AWS::IAM::User"]
- }
-
- source {
- owner = "CUSTOM_LAMBDA"
- source_identifier = module.lambda_iam_mfa_for_console_access.function_arn
-
- source_detail {
- message_type = "ConfigurationItemChangeNotification"
- }
-
- source_detail {
- message_type = "OversizedConfigurationItemChangeNotification"
- }
- }
-
- depends_on = [
- aws_lambda_permission.iam_mfa_for_console_access,
- null_resource.dependencies,
- ]
+data "aws_partition" "this" {
+ count = local.custom_lambda ? 1 : 0
}
-### RESTRICTED COMMON PORTS: ACCESS
-resource "aws_config_config_rule" "restricted_common_ports_access" {
- count = var.create_config_rules && ! local.exclude_restricted_common_ports_access ? 1 : 0
-
- name = "restricted-common-ports-access"
- description = "Checks whether security groups that are in use disallow unrestricted incoming TCP traffic to the specified ports."
-
- input_parameters = <<-EOF
- {
- "blockedPort1": "22",
- "blockedPort2": "3389"
- }
- EOF
-
- scope {
- compliance_resource_types = ["AWS::EC2::SecurityGroup"]
- }
+data "aws_iam_policy" "custom_lambda" {
+ count = local.custom_lambda ? 1 : 0
- source {
- owner = "AWS"
- source_identifier = "RESTRICTED_INCOMING_TRAFFIC"
- }
-
- depends_on = [null_resource.dependencies]
+ arn = "arn:${data.aws_partition.this[0].partition}:iam::aws:policy/service-role/AWSConfigRulesExecutionRole"
}
-### RESTRICTED COMMON PORTS: DATABASE
-resource "aws_config_config_rule" "restricted_common_ports_database" {
- count = var.create_config_rules && ! local.exclude_restricted_common_ports_database ? 1 : 0
-
- name = "restricted-common-ports-database"
- description = "Checks whether security groups that are in use disallow unrestricted incoming TCP traffic to the specified ports."
-
- input_parameters = <<-EOF
- {
- "blockedPort1": "1433",
- "blockedPort2": "1521",
- "blockedPort3": "3306",
- "blockedPort4": "4333",
- "blockedPort5": "5432"
- }
- EOF
-
- scope {
- compliance_resource_types = ["AWS::EC2::SecurityGroup"]
- }
+data "aws_iam_policy_document" "custom_lambda" {
+ count = local.custom_lambda ? 1 : 0
- source {
- owner = "AWS"
- source_identifier = "RESTRICTED_INCOMING_TRAFFIC"
- }
-
- depends_on = [null_resource.dependencies]
+ source_json = data.aws_iam_policy.custom_lambda[0].policy
+ override_json = var.lambda.policy
}
-### EBS SNAPSHOT PUBLIC RESTORABLE
-resource "aws_config_config_rule" "ebs_snapshot_public_restorable_check" {
- count = var.create_config_rules && ! local.exclude_ebs_snapshot_public_restorable_check ? 1 : 0
-
- name = "ebs-snapshot-public-restorable-check"
- description = "Checks whether Amazon Elastic Block Store (Amazon EBS) snapshots are not publicly restorable. The rule is NON_COMPLIANT if one or more snapshots with RestorableByUserIds field are set to all, that is, Amazon EBS snapshots are public."
- input_parameters = "{}"
- maximum_execution_frequency = "TwentyFour_Hours"
-
- source {
- owner = "AWS"
- source_identifier = "EBS_SNAPSHOT_PUBLIC_RESTORABLE_CHECK"
- }
-
- depends_on = [null_resource.dependencies]
+locals {
+ custom_lambda = var.config_rule.owner == "CUSTOM_LAMBDA"
}
diff --git a/outputs.tf b/outputs.tf
new file mode 100644
index 0000000..927bcd6
--- /dev/null
+++ b/outputs.tf
@@ -0,0 +1,4 @@
+output "config_rule" {
+ description = "AWS Config Rule object"
+ value = aws_config_config_rule.this
+}
diff --git a/tests/create_aws_config_rule/main.tf b/tests/create_aws_config_rule/main.tf
new file mode 100644
index 0000000..8fcaab3
--- /dev/null
+++ b/tests/create_aws_config_rule/main.tf
@@ -0,0 +1,56 @@
+provider aws {
+ region = "us-east-1"
+}
+
+module "create_config_rules" {
+ source = "../../"
+
+ providers = {
+ aws = aws
+ }
+
+ config_rule = {
+ description = "Checks that your Amazon S3 buckets do not allow public read access. The rule checks the Block Public Access settings, the bucket policy, and the bucket access control list (ACL)"
+ input_parameters = null
+ maximum_execution_frequency = null
+ name = "s3-bucket-public-read-prohibited"
+ owner = "AWS"
+ scope = null
+ source_details = null
+ source_identifier = "S3_BUCKET_PUBLIC_READ_PROHIBITED"
+ tags = {}
+ }
+
+ config_recorder_id = module.config.config_recorder_id
+}
+
+module "config" {
+ source = "git::https://github.com/plus3it/terraform-aws-tardigrade-config.git?ref=1.0.7"
+
+ providers = {
+ aws = aws
+ }
+
+ create_config = true
+ account_id = data.aws_caller_identity.this.account_id
+ config_bucket = aws_s3_bucket.this.id
+}
+
+resource "aws_s3_bucket" "this" {
+ bucket = "tardigrade-config-rules-${random_string.this.result}"
+ force_destroy = true
+}
+
+resource "random_string" "this" {
+ length = 6
+ number = false
+ special = false
+ upper = false
+}
+
+data "aws_caller_identity" "this" {}
+
+output "config_rule" {
+ description = "AWS Config Rule object"
+ value = module.create_config_rules.config_rule
+}
diff --git a/tests/create_config_recorder/README.md b/tests/create_config_recorder/README.md
deleted file mode 100644
index 04af3ad..0000000
--- a/tests/create_config_recorder/README.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Create Config Recorder Test
-
-
-
-## Requirements
-
-| Name | Version |
-|------|---------|
-| terraform | >= 0.12 |
-
-## Providers
-
-| Name | Version |
-|------|---------|
-| terraform | n/a |
-
-## Inputs
-
-No input.
-
-## Outputs
-
-No output.
-
-
diff --git a/tests/create_config_recorder/main.tf b/tests/create_config_recorder/main.tf
deleted file mode 100644
index 727c59d..0000000
--- a/tests/create_config_recorder/main.tf
+++ /dev/null
@@ -1,23 +0,0 @@
-provider aws {
- region = "us-east-1"
-}
-
-data "terraform_remote_state" "prereq" {
- backend = "local"
- config = {
- path = "prereq/terraform.tfstate"
- }
-}
-
-module "create_config_recorder" {
- source = "../../"
- providers = {
- aws = aws
- }
-
- create_config_rules = true
- cloudtrail_bucket = data.terraform_remote_state.prereq.outputs.cloudtrail_bucket
- config_bucket = data.terraform_remote_state.prereq.outputs.config_bucket
- config_sns_topic_arn = data.terraform_remote_state.prereq.outputs.sns_topic
- config_recorder = data.terraform_remote_state.prereq.outputs.config_recorder
-}
diff --git a/tests/create_config_recorder/prereq/README.md b/tests/create_config_recorder/prereq/README.md
deleted file mode 100644
index f37ec31..0000000
--- a/tests/create_config_recorder/prereq/README.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# PreReq
-
-
-
-## Requirements
-
-| Name | Version |
-|------|---------|
-| terraform | >= 0.12 |
-
-## Providers
-
-| Name | Version |
-|------|---------|
-| aws | n/a |
-| random | n/a |
-
-## Inputs
-
-No input.
-
-## Outputs
-
-| Name | Description |
-|------|-------------|
-| cloudtrail\_bucket | n/a |
-| config\_bucket | n/a |
-| config\_recorder | n/a |
-| sns\_topic | n/a |
-
-
diff --git a/tests/create_config_recorder/prereq/main.tf b/tests/create_config_recorder/prereq/main.tf
deleted file mode 100644
index 6460632..0000000
--- a/tests/create_config_recorder/prereq/main.tf
+++ /dev/null
@@ -1,98 +0,0 @@
-provider aws {
- region = "us-east-1"
-}
-
-data "aws_partition" "current" {}
-data "aws_caller_identity" "current" {}
-
-data "aws_iam_policy_document" "config_assume_role" {
- statement {
- actions = ["sts:AssumeRole"]
-
- principals {
- type = "Service"
- identifiers = ["config.amazonaws.com"]
- }
- }
-}
-
-data "aws_iam_policy_document" "config" {
- statement {
- actions = ["s3:PutObject*"]
- resources = ["arn:${data.aws_partition.current.partition}:s3:::${aws_s3_bucket.this.id}/AWSLogs/${data.aws_caller_identity.current.account_id}/*"]
-
- condition {
- test = "StringLike"
- variable = "s3:x-amz-acl"
- values = ["bucket-owner-full-control"]
- }
- }
-
- statement {
- actions = ["s3:GetBucketAcl"]
- resources = ["arn:${data.aws_partition.current.partition}:s3:::${aws_s3_bucket.this.id}"]
- }
-}
-
-resource "random_string" "this" {
- length = 6
- number = false
- special = false
- upper = false
-}
-
-resource "aws_iam_role" "this" {
- name = "tardigrade-config-rules-${random_string.this.result}"
- assume_role_policy = data.aws_iam_policy_document.config_assume_role.json
-}
-
-resource "aws_iam_role_policy" "this" {
- name = "tardigrade-config-rules-${random_string.this.result}"
- role = aws_iam_role.this.id
- policy = data.aws_iam_policy_document.config.json
-}
-
-resource "aws_iam_role_policy_attachment" "this" {
- role = aws_iam_role.this.name
- policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/service-role/AWSConfigRole"
-}
-
-resource "aws_config_configuration_recorder" "this" {
- name = "tardigrade-config-recorder-${random_string.this.result}"
- role_arn = aws_iam_role.this.arn
-
- recording_group {
- all_supported = "true"
- include_global_resource_types = "true"
- }
-
- depends_on = [
- aws_iam_role_policy.this,
- aws_iam_role_policy_attachment.this,
- ]
-}
-
-resource "aws_s3_bucket" "this" {
- bucket = "tardigrade-config-rules-${random_string.this.result}"
- force_destroy = true
-}
-
-resource "aws_sns_topic" "this" {
- name = "tardigrade-config-rules-${random_string.this.result}"
-}
-
-output "config_bucket" {
- value = aws_s3_bucket.this.id
-}
-
-output "cloudtrail_bucket" {
- value = aws_s3_bucket.this.id
-}
-
-output "config_recorder" {
- value = aws_config_configuration_recorder.this.id
-}
-
-output "sns_topic" {
- value = aws_sns_topic.this.arn
-}
diff --git a/tests/create_config_recorder/prereq/versions.tf b/tests/create_config_recorder/prereq/versions.tf
deleted file mode 100644
index d9b6f79..0000000
--- a/tests/create_config_recorder/prereq/versions.tf
+++ /dev/null
@@ -1,3 +0,0 @@
-terraform {
- required_version = ">= 0.12"
-}
diff --git a/tests/create_config_recorder/versions.tf b/tests/create_config_recorder/versions.tf
deleted file mode 100644
index d9b6f79..0000000
--- a/tests/create_config_recorder/versions.tf
+++ /dev/null
@@ -1,3 +0,0 @@
-terraform {
- required_version = ">= 0.12"
-}
diff --git a/tests/create_custom_config_rule/main.tf b/tests/create_custom_config_rule/main.tf
new file mode 100644
index 0000000..1df4923
--- /dev/null
+++ b/tests/create_custom_config_rule/main.tf
@@ -0,0 +1,94 @@
+provider aws {
+ region = "us-east-1"
+}
+
+module "create_config_rules" {
+ source = "../../"
+
+ providers = {
+ aws = aws
+ }
+
+ config_recorder_id = module.config.config_recorder_id
+
+ config_rule = {
+ description = "Checks that IAM User Access Keys have been rotated within the specified number of days"
+ maximum_execution_frequency = null
+ name = "iam-access-key-rotation-check"
+ owner = "CUSTOM_LAMBDA"
+ source_identifier = null
+ tags = {}
+
+ input_parameters = jsonencode({
+ MaximumAccessKeyAge = "90"
+ })
+
+ source_details = [
+ {
+ message_type = "ConfigurationItemChangeNotification"
+ },
+ {
+ message_type = "OversizedConfigurationItemChangeNotification"
+ },
+ ]
+
+ scope = {
+ compliance_resource_types = [
+ "AWS::IAM::User",
+ ]
+ compliance_resource_id = null
+ tag_key = null
+ tag_value = null
+ }
+ }
+
+ lambda = {
+ description = "Checks that IAM User Access Keys have been rotated within the specified number of days"
+ handler = "iam_access_key_rotation-triggered.handler"
+ name = "config_rule_iam_access_key_rotation_check"
+ policy = data.aws_iam_policy_document.lambda_iam_access_key_rotation_check.json
+ runtime = "nodejs10.x"
+ source_path = "${path.module}/.terraform/modules/create_config_rules.vendor/node/iam_access_key_rotation-triggered.js"
+ reserved_concurrent_executions = -1
+ tags = {}
+ timeout = 15
+ }
+}
+
+module "config" {
+ source = "git::https://github.com/plus3it/terraform-aws-tardigrade-config.git?ref=1.0.7"
+
+ providers = {
+ aws = aws
+ }
+
+ create_config = true
+ account_id = data.aws_caller_identity.this.account_id
+ config_bucket = aws_s3_bucket.this.id
+}
+
+resource "aws_s3_bucket" "this" {
+ bucket = "tardigrade-config-rules-${random_string.this.result}"
+ force_destroy = true
+}
+
+resource "random_string" "this" {
+ length = 6
+ number = false
+ special = false
+ upper = false
+}
+
+data "aws_caller_identity" "this" {}
+
+data "aws_iam_policy_document" "lambda_iam_access_key_rotation_check" {
+ statement {
+ actions = ["iam:ListAccessKeys"]
+ resources = ["*"]
+ }
+}
+
+output "config_rule" {
+ description = "AWS Config Rule object"
+ value = module.create_config_rules.config_rule
+}
diff --git a/tests/create_legacy_config_rules/main.tf b/tests/create_legacy_config_rules/main.tf
new file mode 100644
index 0000000..dd347be
--- /dev/null
+++ b/tests/create_legacy_config_rules/main.tf
@@ -0,0 +1,365 @@
+provider aws {
+ region = "us-east-1"
+}
+
+module "create_config_rules" {
+ source = "../../"
+ for_each = local.config_rules
+
+ providers = {
+ aws = aws
+ }
+
+ config_recorder_id = module.config.config_recorder_id
+ config_rule = each.value
+ lambda = try(local.lambdas[each.key], null)
+}
+
+module "config" {
+ source = "git::https://github.com/plus3it/terraform-aws-tardigrade-config.git?ref=1.0.7"
+
+ providers = {
+ aws = aws
+ }
+
+ create_config = true
+ account_id = data.aws_caller_identity.this.account_id
+ config_bucket = aws_s3_bucket.this.id
+}
+
+resource "aws_s3_bucket" "this" {
+ bucket = "tardigrade-config-rules-${random_string.this.result}"
+ force_destroy = true
+}
+
+resource "aws_sns_topic" "this" {
+ name = "tardigrade-config-rules-${random_string.this.result}"
+}
+
+resource "random_string" "this" {
+ length = 6
+ number = false
+ special = false
+ upper = false
+}
+
+data "aws_caller_identity" "this" {}
+
+data "aws_iam_policy_document" "lambda_iam_access_key_rotation_check" {
+ statement {
+ actions = ["iam:ListAccessKeys"]
+ resources = ["*"]
+ }
+}
+
+data "aws_iam_policy_document" "lambda_iam_mfa_for_console_access" {
+ statement {
+ actions = [
+ "iam:ListMFADevices",
+ "iam:GetLoginProfile",
+ ]
+
+ resources = ["*"]
+ }
+}
+
+locals {
+ config_rules = {
+ cloudtrail-enabled = merge(local.defaults.config_rule, {
+ description = "Checks whether AWS CloudTrail is enabled in your AWS account. Optionally, you can specify which S3 bucket, SNS topic, and Amazon CloudWatch Logs ARN to use"
+ name = "cloudtrail-enabled"
+ owner = "AWS"
+ source_identifier = "CLOUD_TRAIL_ENABLED"
+
+ input_parameters = jsonencode({
+ s3BucketName = aws_s3_bucket.this.id,
+ })
+ })
+
+ iam-password-policy = merge(local.defaults.config_rule, {
+ description = "Checks whether the account password policy for IAM users meets the specified requirements"
+ name = "iam-password-policy"
+ owner = "AWS"
+ source_identifier = "IAM_PASSWORD_POLICY"
+
+ input_parameters = jsonencode({
+ RequireUppercaseCharacters = "true",
+ RequireLowercaseCharacters = "true",
+ RequireSymbols = "true",
+ RequireNumbers = "true",
+ MinimumPasswordLength = "14",
+ PasswordReusePrevention = "24",
+ MaxPasswordAge = "60"
+ })
+ })
+
+ s3-bucket-public-read-prohibited = merge(local.defaults.config_rule, {
+ description = "Checks that your Amazon S3 buckets do not allow public read access. The rule checks the Block Public Access settings, the bucket policy, and the bucket access control list (ACL)"
+ name = "s3-bucket-public-read-prohibited"
+ owner = "AWS"
+ source_identifier = "S3_BUCKET_PUBLIC_READ_PROHIBITED"
+ })
+
+ s3-bucket-public-write-prohibited = merge(local.defaults.config_rule, {
+ description = "Checks that your Amazon S3 buckets do not allow public write access. The rule checks the Block Public Access settings, the bucket policy, and the bucket access control list (ACL)"
+ name = "s3-bucket-public-write-prohibited"
+ owner = "AWS"
+ source_identifier = "S3_BUCKET_PUBLIC_WRITE_PROHIBITED"
+ })
+
+ s3-bucket-ssl-requests-only = merge(local.defaults.config_rule, {
+ description = "Checks whether S3 buckets have policies that require requests to use Secure Socket Layer (SSL)"
+ name = "s3-bucket-ssl-requests-only"
+ owner = "AWS"
+ source_identifier = "S3_BUCKET_SSL_REQUESTS_ONLY"
+ })
+
+ codebuild-project-envvar-awscred-check = merge(local.defaults.config_rule, {
+ description = "Checks whether the project contains environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. The rule is NON_COMPLIANT when the project environment variables contains plaintext credentials"
+ name = "codebuild-project-envvar-awscred-check"
+ owner = "AWS"
+ source_identifier = "CODEBUILD_PROJECT_ENVVAR_AWSCRED_CHECK"
+ })
+
+ codebuild-project-source-repo-url-check = merge(local.defaults.config_rule, {
+ description = "Checks whether the GitHub or Bitbucket source repository URL contains either personal access tokens or user name and password. The rule is COMPLIANT with the usage of OAuth to grant authorization for accessing GitHub or Bitbucket repositories"
+ name = "codebuild-project-source-repo-url-check"
+ owner = "AWS"
+ source_identifier = "CODEBUILD_PROJECT_SOURCE_REPO_URL_CHECK"
+ })
+
+ instances-in-vpc = merge(local.defaults.config_rule, {
+ description = "Checks whether the GitHub or Bitbucket source repository URL contains either personal access tokens or user name and password. The rule is COMPLIANT with the usage of OAuth to grant authorization for accessing GitHub or Bitbucket repositories"
+ name = "instances-in-vpc"
+ owner = "AWS"
+ source_identifier = "INSTANCES_IN_VPC"
+ })
+
+ ec2-volume-inuse-check = merge(local.defaults.config_rule, {
+ description = "Checks whether EBS volumes are attached to EC2 instances. Optionally checks if EBS volumes are marked for deletion when an instance is terminated"
+ name = "ec2-volume-inuse-check"
+ owner = "AWS"
+ source_identifier = "EC2_VOLUME_INUSE_CHECK"
+ })
+
+ eip-attached = merge(local.defaults.config_rule, {
+ description = "Checks whether all Elastic IP addresses that are allocated to a VPC are attached to EC2 instances or in-use elastic network interfaces (ENIs)"
+ name = "eip-attached"
+ owner = "AWS"
+ source_identifier = "EIP_ATTACHED"
+ })
+
+ lambda-function-public-access-prohibited = merge(local.defaults.config_rule, {
+ description = "Checks whether the AWS Lambda function policy attached to the Lambda resource prohibits public access. If the Lambda function policy allows public access it is NON_COMPLIANT"
+ name = "lambda-function-public-access-prohibited"
+ owner = "AWS"
+ source_identifier = "LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED"
+ })
+
+ root-account-mfa-enabled = merge(local.defaults.config_rule, {
+ description = "Checks whether users of your AWS account require a multi-factor authentication (MFA) device to sign in with root credentials"
+ name = "root-account-mfa-enabled"
+ owner = "AWS"
+ source_identifier = "ROOT_ACCOUNT_MFA_ENABLED"
+ })
+
+ restricted-common-ports-access = merge(local.defaults.config_rule, {
+ description = "Checks whether security groups that are in use disallow unrestricted incoming TCP traffic to the specified ports"
+ name = "restricted-common-ports-access"
+ owner = "AWS"
+ source_identifier = "RESTRICTED_INCOMING_TRAFFIC"
+
+ input_parameters = jsonencode({
+ blockedPort1 = "22"
+ blockedPort2 = "3389"
+ })
+
+ scope = merge(local.defaults.scope, {
+ compliance_resource_types = [
+ "AWS::IAM::SecurityGroup",
+ ]
+ })
+ })
+
+ restricted-common-ports-database = merge(local.defaults.config_rule, {
+ description = "Checks whether security groups that are in use disallow unrestricted incoming TCP traffic to the specified ports"
+ name = "restricted-common-ports-database"
+ owner = "AWS"
+ source_identifier = "RESTRICTED_INCOMING_TRAFFIC"
+
+ input_parameters = jsonencode({
+ blockedPort1 = "1433"
+ blockedPort2 = "1521"
+ blockedPort3 = "3306"
+ blockedPort4 = "4333"
+ blockedPort5 = "5432"
+ })
+
+ scope = merge(local.defaults.scope, {
+ compliance_resource_types = [
+ "AWS::IAM::SecurityGroup",
+ ]
+ })
+ })
+
+ ebs-snapshot-public-restorable-check = merge(local.defaults.config_rule, {
+ description = "Checks whether Amazon Elastic Block Store (Amazon EBS) snapshots are not publicly restorable. The rule is NON_COMPLIANT if one or more snapshots with RestorableByUserIds field are set to all, that is, Amazon EBS snapshots are public"
+ maximum_execution_frequency = "TwentyFour_Hours"
+ name = "ebs-snapshot-public-restorable-check"
+ owner = "AWS"
+ source_identifier = "EBS_SNAPSHOT_PUBLIC_RESTORABLE_CHECK"
+ })
+
+ iam-user-active = merge(local.defaults.config_rule, {
+ description = "Checks whether your AWS Identity and Access Management (IAM) users have passwords or active access keys that have not been used within the specified number of days you provided"
+ name = "iam-user-active"
+ owner = "AWS"
+ source_identifier = "IAM_USER_UNUSED_CREDENTIALS_CHECK"
+
+ input_parameters = jsonencode({
+ maxCredentialUsageAge = "90"
+ })
+ })
+
+
+ iam-access-key-rotation-check = merge(local.defaults.config_rule, {
+ description = "Checks that IAM User Access Keys have been rotated within the specified number of days"
+ name = "iam-access-key-rotation-check"
+ owner = "CUSTOM_LAMBDA"
+ source_details = local.source_details.configuration_change
+
+ input_parameters = jsonencode({
+ MaximumAccessKeyAge = "90"
+ })
+
+ scope = merge(local.defaults.scope, {
+ compliance_resource_types = [
+ "AWS::IAM::User",
+ ]
+ })
+ })
+
+ rds-vpc-public-subnet = merge(local.defaults.config_rule, {
+ description = "Checks that no RDS Instances are in a Public Subnet"
+ name = "rds-vpc-public-subnet"
+ owner = "CUSTOM_LAMBDA"
+ source_details = local.source_details.configuration_change
+
+ scope = merge(local.defaults.scope, {
+ compliance_resource_types = [
+ "AWS::RDS::DBInstance",
+ ]
+ })
+ })
+
+ config-enabled = merge(local.defaults.config_rule, {
+ description = "Checks that Config has been activated and is logging to a specific bucket and sending to a specifc SNS topic"
+ name = "config-enabled"
+ owner = "CUSTOM_LAMBDA"
+ source_details = local.source_details.scheduled_notification
+
+ input_parameters = jsonencode({
+ s3BucketName = aws_s3_bucket.this.id
+ snsTopicARN = aws_sns_topic.this.arn
+ })
+ })
+
+ iam-mfa-for-console-access = merge(local.defaults.config_rule, {
+ description = "Checks that all IAM users with console access have at least one MFA device"
+ name = "iam-mfa-for-console-access"
+ owner = "CUSTOM_LAMBDA"
+ source_details = local.source_details.configuration_change
+
+ scope = merge(local.defaults.scope, {
+ compliance_resource_types = [
+ "AWS::IAM::User",
+ ]
+ })
+ })
+ }
+
+ lambdas = {
+ iam-access-key-rotation-check = merge(local.defaults.lambda, {
+ description = "Checks that IAM User Access Keys have been rotated within the specified number of days"
+ handler = "iam_access_key_rotation-triggered.handler"
+ name = "config_rule_iam_access_key_rotation_check"
+ policy = data.aws_iam_policy_document.lambda_iam_access_key_rotation_check.json
+ runtime = "nodejs10.x"
+ source_path = "${local.source_path}/node/iam_access_key_rotation-triggered.js"
+ })
+
+ rds-vpc-public-subnet = merge(local.defaults.lambda, {
+ description = "Checks that no RDS Instances are in a Public Subnet"
+ handler = "rds_vpc_public_subnet.lambda_handler"
+ name = "config_rule_rds_vpc_public_subnet"
+ runtime = "python3.6"
+ source_path = "${local.source_path}/python/rds_vpc_public_subnet.py"
+ })
+
+ config-enabled = merge(local.defaults.lambda, {
+ description = "Checks that Config has been activated and is logging to a specific bucket and sending to a specifc SNS topic"
+ handler = "config_enabled.lambda_handler"
+ name = "config_rule_config_enabled"
+ runtime = "python3.6"
+ source_path = "${local.source_path}/python/config_enabled.py"
+ })
+
+ iam-mfa-for-console-access = merge(local.defaults.lambda, {
+ description = "Checks that all IAM users with console access have at least one MFA device"
+ handler = "iam_mfa_for_console_access.lambda_handler"
+ name = "config_rule_iam_mfa_for_console_access"
+ policy = data.aws_iam_policy_document.lambda_iam_mfa_for_console_access.json
+ runtime = "python3.6"
+ source_path = "${local.source_path}/python/iam_mfa_for_console_access.py"
+ })
+ }
+
+ source_path = "${path.module}/.terraform/modules/create_config_rules.vendor"
+
+ defaults = {
+ config_rule = {
+ input_parameters = null
+ maximum_execution_frequency = null
+ scope = null
+ source_details = null
+ source_identifier = null
+ tags = null
+ }
+
+ scope = {
+ compliance_resource_types = null
+ compliance_resource_id = null
+ tag_key = null
+ tag_value = null
+ }
+
+ lambda = {
+ policy = null
+ reserved_concurrent_executions = -1
+ tags = null
+ timeout = 15
+ }
+ }
+
+ source_details = {
+ configuration_change = [
+ {
+ message_type = "ConfigurationItemChangeNotification"
+ },
+ {
+ message_type = "OversizedConfigurationItemChangeNotification"
+ },
+ ]
+
+ scheduled_notification = [
+ {
+ message_type = "ScheduledNotification"
+ },
+ ]
+ }
+}
+
+output "create_config_rules" {
+ description = "Module object from create_config_rules"
+ value = module.create_config_rules
+}
diff --git a/tests/go.mod b/tests/go.mod
deleted file mode 100644
index ac21db9..0000000
--- a/tests/go.mod
+++ /dev/null
@@ -1,5 +0,0 @@
-module terraform-aws-tardigrade-config-rules/tests
-
-go 1.13
-
-require github.com/gruntwork-io/terratest v0.29.0
diff --git a/tests/go.sum b/tests/go.sum
deleted file mode 100644
index dee0c2f..0000000
--- a/tests/go.sum
+++ /dev/null
@@ -1,632 +0,0 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
-cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
-cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
-cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw=
-cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
-cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
-cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
-cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
-dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/Azure/azure-sdk-for-go v32.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/azure-sdk-for-go v38.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/azure-sdk-for-go v38.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
-github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
-github.com/Azure/go-autorest/autorest v0.9.1/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
-github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
-github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
-github.com/Azure/go-autorest/autorest/adal v0.6.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
-github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
-github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
-github.com/Azure/go-autorest/autorest/azure/auth v0.3.0/go.mod h1:CI4BQYBct8NS7BXNBBX+RchsFsUu5+oz+OSyR/ZIi7U=
-github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM=
-github.com/Azure/go-autorest/autorest/azure/cli v0.3.0/go.mod h1:rNYMNAefZMRowqCV0cVhr/YDW5dD7afFq9nXAXL4ykE=
-github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw=
-github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
-github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
-github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
-github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
-github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
-github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc=
-github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
-github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
-github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
-github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
-github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14=
-github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
-github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
-github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
-github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
-github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
-github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.23.8/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
-github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
-github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
-github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
-github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
-github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
-github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
-github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
-github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
-github.com/docker/cli v0.0.0-20200109221225-a4f60165b7a3/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
-github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
-github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
-github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
-github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
-github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
-github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
-github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
-github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
-github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
-github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
-github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
-github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
-github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
-github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
-github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
-github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
-github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
-github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
-github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
-github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
-github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
-github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
-github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-containerregistry v0.0.0-20200110202235-f4fb41bf00a3/go.mod h1:2wIuQute9+hhWqvL3vEI7YB0EKluF4WcPzI1eAliazk=
-github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
-github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
-github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
-github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
-github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
-github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
-github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
-github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
-github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/gruntwork-io/gruntwork-cli v0.5.1/go.mod h1:IBX21bESC1/LGoV7jhXKUnTQTZgQ6dYRsoj/VqxUSZQ=
-github.com/gruntwork-io/terratest v0.22.2 h1:rIuiDbe5Ur+EYePWtqtzsdXojUfJECXdD92hQRKY5sk=
-github.com/gruntwork-io/terratest v0.22.2/go.mod h1:veL2PxiS2Z5S63ERnyiIY/sxgmXJpZD8YZybiqvduwQ=
-github.com/gruntwork-io/terratest v0.22.3 h1:MReIa0Fz/BYe31A9SlCO9SIarO2pWNjlcDed6JAZASo=
-github.com/gruntwork-io/terratest v0.22.3/go.mod h1:veL2PxiS2Z5S63ERnyiIY/sxgmXJpZD8YZybiqvduwQ=
-github.com/gruntwork-io/terratest v0.22.4 h1:6VQOgtmPjoAIhQ33Ac4qDj71ZwkrCenFbPGF9Sl7ik0=
-github.com/gruntwork-io/terratest v0.22.4/go.mod h1:+fVff0FQYuRzCF3LKpKF9ac+4w384LDcwLZt7O/KmEE=
-github.com/gruntwork-io/terratest v0.23.0 h1:JmGeqO0r5zRLAV55T67NEmPZArz9lN3RKd0moAKhIT4=
-github.com/gruntwork-io/terratest v0.23.0/go.mod h1:+fVff0FQYuRzCF3LKpKF9ac+4w384LDcwLZt7O/KmEE=
-github.com/gruntwork-io/terratest v0.23.1 h1:FIvcs7xH/JH7Knlw5bUAScAd2eXU+G+P7p6CbZi977s=
-github.com/gruntwork-io/terratest v0.23.1/go.mod h1:+fVff0FQYuRzCF3LKpKF9ac+4w384LDcwLZt7O/KmEE=
-github.com/gruntwork-io/terratest v0.23.2 h1:vR32uRoXcsPgNKMnfTxnBe9Ed9Y0CyR0wVda9UgzC1E=
-github.com/gruntwork-io/terratest v0.23.2/go.mod h1:+fVff0FQYuRzCF3LKpKF9ac+4w384LDcwLZt7O/KmEE=
-github.com/gruntwork-io/terratest v0.23.3 h1:zf3K7Z2g88yMgiZiJja5vAWsig8tRqG537Mp9S+qrFc=
-github.com/gruntwork-io/terratest v0.23.3/go.mod h1:+fVff0FQYuRzCF3LKpKF9ac+4w384LDcwLZt7O/KmEE=
-github.com/gruntwork-io/terratest v0.23.4 h1:3H8/gS4XJvy3AwPyvil3yMMeiBB6FrGP9IvJI6e2uis=
-github.com/gruntwork-io/terratest v0.23.4/go.mod h1:ds4v1EDndcBq3zNUPs1uot0YGWDbk++I5KPSOSJ6df4=
-github.com/gruntwork-io/terratest v0.23.5 h1:BNTc2uUMT/YuYv+l3TKQcvdbXQNk+tk+Db0PED9cbdw=
-github.com/gruntwork-io/terratest v0.23.5/go.mod h1:ds4v1EDndcBq3zNUPs1uot0YGWDbk++I5KPSOSJ6df4=
-github.com/gruntwork-io/terratest v0.24.1 h1:aTdBGUkUDHUW6v7zYhMpk8PD8YnCCJT74MqwUIpXv7o=
-github.com/gruntwork-io/terratest v0.24.1/go.mod h1:0MCPUGIgQaAXOmw0qRLqyIXs8q6yoNPB3aZt4SkdH0M=
-github.com/gruntwork-io/terratest v0.24.2 h1:ZL7s7ZaVPRds+HqtPFh8gXjFVpKRNAAbwyVPYx3lH50=
-github.com/gruntwork-io/terratest v0.24.2/go.mod h1:0MCPUGIgQaAXOmw0qRLqyIXs8q6yoNPB3aZt4SkdH0M=
-github.com/gruntwork-io/terratest v0.25.0 h1:4Sz8q5DVEqeCC+eAfN+apL/PnqsmurGsn+fGRnZGeKU=
-github.com/gruntwork-io/terratest v0.25.0/go.mod h1:0MCPUGIgQaAXOmw0qRLqyIXs8q6yoNPB3aZt4SkdH0M=
-github.com/gruntwork-io/terratest v0.25.1 h1:iRjRwya0hkq+pxqkZPDj9k4w/Qi+q7LZInns7e4g32U=
-github.com/gruntwork-io/terratest v0.25.1/go.mod h1:0MCPUGIgQaAXOmw0qRLqyIXs8q6yoNPB3aZt4SkdH0M=
-github.com/gruntwork-io/terratest v0.25.2 h1:UUdWzWgljehlwz88RD/AjSdn68UiTXrFQ7x+Be9gXAw=
-github.com/gruntwork-io/terratest v0.25.2/go.mod h1:0MCPUGIgQaAXOmw0qRLqyIXs8q6yoNPB3aZt4SkdH0M=
-github.com/gruntwork-io/terratest v0.26.0 h1:RFDr6nh/zn3rN1bZjZ9kYrEXIKof5T0AMEjgn+tXETw=
-github.com/gruntwork-io/terratest v0.26.0/go.mod h1:ONEOU6Fv3a1rN16Z5t5yWbV57DkVC7665yRyvu3aWnk=
-github.com/gruntwork-io/terratest v0.26.1 h1:T+R6sxhr4hTkbhOl9EIp/xTJm6070YcM5sCG0tR4xXg=
-github.com/gruntwork-io/terratest v0.26.1/go.mod h1:ONEOU6Fv3a1rN16Z5t5yWbV57DkVC7665yRyvu3aWnk=
-github.com/gruntwork-io/terratest v0.26.2 h1:Q2909tK8EihfT3144SI/MxFmVLmEwtagnHVFRagiVAg=
-github.com/gruntwork-io/terratest v0.26.2/go.mod h1:ONEOU6Fv3a1rN16Z5t5yWbV57DkVC7665yRyvu3aWnk=
-github.com/gruntwork-io/terratest v0.26.3 h1:2ECsZ5XwEZ685uIvraMCc/42NGNThidZJNmFrHLzdD0=
-github.com/gruntwork-io/terratest v0.26.3/go.mod h1:ONEOU6Fv3a1rN16Z5t5yWbV57DkVC7665yRyvu3aWnk=
-github.com/gruntwork-io/terratest v0.26.4 h1:KjQbDAXm8vgGe41iFQVh45kquxF3xIGTPvpZhIK/GO0=
-github.com/gruntwork-io/terratest v0.26.4/go.mod h1:ONEOU6Fv3a1rN16Z5t5yWbV57DkVC7665yRyvu3aWnk=
-github.com/gruntwork-io/terratest v0.26.5 h1:dZpFKHF/uG6nrNY0lfZSvcF+GV5cfl+7sHsYhfe/oWU=
-github.com/gruntwork-io/terratest v0.26.5/go.mod h1:ONEOU6Fv3a1rN16Z5t5yWbV57DkVC7665yRyvu3aWnk=
-github.com/gruntwork-io/terratest v0.26.6 h1:blOAXLQFIN0nR3BTlg0LDG1Hmj15Q9wo790RS6bMjV4=
-github.com/gruntwork-io/terratest v0.26.6/go.mod h1:ONEOU6Fv3a1rN16Z5t5yWbV57DkVC7665yRyvu3aWnk=
-github.com/gruntwork-io/terratest v0.27.0 h1:BRsgWUGQHS6XC4beDe0b5ToeKp40hZCq0+h/awA6fu8=
-github.com/gruntwork-io/terratest v0.27.0/go.mod h1:ONEOU6Fv3a1rN16Z5t5yWbV57DkVC7665yRyvu3aWnk=
-github.com/gruntwork-io/terratest v0.27.1 h1:cUmpCwK+zmhmSEmjwKiM406uTRBp9UTq0lxNeZmOND8=
-github.com/gruntwork-io/terratest v0.27.1/go.mod h1:ONEOU6Fv3a1rN16Z5t5yWbV57DkVC7665yRyvu3aWnk=
-github.com/gruntwork-io/terratest v0.27.2 h1:T3TXpOWSHP/3hR4EWHuv/byWkkHuHGjA1ycVrdf3ThQ=
-github.com/gruntwork-io/terratest v0.27.2/go.mod h1:ONEOU6Fv3a1rN16Z5t5yWbV57DkVC7665yRyvu3aWnk=
-github.com/gruntwork-io/terratest v0.27.3 h1:kDTrA+b+OoqDkwLs0A7THgK8WPXyHfuPiZB2XvR5vVU=
-github.com/gruntwork-io/terratest v0.27.3/go.mod h1:ONEOU6Fv3a1rN16Z5t5yWbV57DkVC7665yRyvu3aWnk=
-github.com/gruntwork-io/terratest v0.27.4 h1:+Pgf3pHRPgVjNv0/MyLWwySmKUcmL+wT6fewu6F4sBE=
-github.com/gruntwork-io/terratest v0.27.4/go.mod h1:ONEOU6Fv3a1rN16Z5t5yWbV57DkVC7665yRyvu3aWnk=
-github.com/gruntwork-io/terratest v0.27.5 h1:o+D1Ea65RnQV9e/k57cTpnQprYN+MOXJm9U44TYYWX0=
-github.com/gruntwork-io/terratest v0.27.5/go.mod h1:HJRmpi3vO3QGbYFPnNe5eYjDF3j9INnvXceYq3pKOo4=
-github.com/gruntwork-io/terratest v0.28.0 h1:7Zfr6cJ0xhIQ1gGZZetRibp+QyC5aGc1wXJHj7NuhHY=
-github.com/gruntwork-io/terratest v0.28.0/go.mod h1:HJRmpi3vO3QGbYFPnNe5eYjDF3j9INnvXceYq3pKOo4=
-github.com/gruntwork-io/terratest v0.28.1 h1:WVvr/4b+SKdQ0LygU76SZjrT6D+FihWbcvXKS62BTFs=
-github.com/gruntwork-io/terratest v0.28.1/go.mod h1:HJRmpi3vO3QGbYFPnNe5eYjDF3j9INnvXceYq3pKOo4=
-github.com/gruntwork-io/terratest v0.28.2 h1:M9CMfS/5unIawxYlz55eRDWH16AH522OAJcUc+zsKv4=
-github.com/gruntwork-io/terratest v0.28.2/go.mod h1:lTntpr4gGDzb2YEQ1GTjC5G/xw9ixMwxGmZkPCk1O0A=
-github.com/gruntwork-io/terratest v0.28.3 h1:7nrfpOp35m7l+6y9l8UrR+qHCnrxizjoX/d6HWFyRsY=
-github.com/gruntwork-io/terratest v0.28.3/go.mod h1:lTntpr4gGDzb2YEQ1GTjC5G/xw9ixMwxGmZkPCk1O0A=
-github.com/gruntwork-io/terratest v0.28.4 h1:uUIFB/0cRLJqvh3MM2Uk4JLEH749xg9Zo8hQifEout4=
-github.com/gruntwork-io/terratest v0.28.4/go.mod h1:lTntpr4gGDzb2YEQ1GTjC5G/xw9ixMwxGmZkPCk1O0A=
-github.com/gruntwork-io/terratest v0.28.5 h1:B3Cd45sc18V0Ieaw9JrIl/U27c2mPdwc0pOAF3hGMn4=
-github.com/gruntwork-io/terratest v0.28.5/go.mod h1:lTntpr4gGDzb2YEQ1GTjC5G/xw9ixMwxGmZkPCk1O0A=
-github.com/gruntwork-io/terratest v0.28.6 h1:SCCEEt8HxQWAfvd5kYIhStoMERt+ihg/hyU+jIyIGqk=
-github.com/gruntwork-io/terratest v0.28.6/go.mod h1:lTntpr4gGDzb2YEQ1GTjC5G/xw9ixMwxGmZkPCk1O0A=
-github.com/gruntwork-io/terratest v0.28.7 h1:ByAgxm2beencqQYiV4LDtrz+a9XS7BYZioGtl2GeaQY=
-github.com/gruntwork-io/terratest v0.28.7/go.mod h1:PkVylPuUNmItkfOTwSiFreYA4FkanK8AluBuNeGxQOw=
-github.com/gruntwork-io/terratest v0.28.8 h1:ARuUf7wG4b6kA6zUzRglg9utsLXdQVuk8Ye+k9g0Kbg=
-github.com/gruntwork-io/terratest v0.28.8/go.mod h1:PkVylPuUNmItkfOTwSiFreYA4FkanK8AluBuNeGxQOw=
-github.com/gruntwork-io/terratest v0.28.10 h1:4K3idJK5atavZ7iwY+L+RPCA7lFwPu87dVnoFOaoPzI=
-github.com/gruntwork-io/terratest v0.28.10/go.mod h1:PkVylPuUNmItkfOTwSiFreYA4FkanK8AluBuNeGxQOw=
-github.com/gruntwork-io/terratest v0.28.11 h1:PBNHA+0NSiovDQbSsvQAp1vUWvyldzioaTWj5zvOKDU=
-github.com/gruntwork-io/terratest v0.28.11/go.mod h1:PkVylPuUNmItkfOTwSiFreYA4FkanK8AluBuNeGxQOw=
-github.com/gruntwork-io/terratest v0.28.12 h1:XPLiZMV0XkRGNj1JRVvvZ2wj99tiQ4CWYfDtPI9cBlI=
-github.com/gruntwork-io/terratest v0.28.12/go.mod h1:PkVylPuUNmItkfOTwSiFreYA4FkanK8AluBuNeGxQOw=
-github.com/gruntwork-io/terratest v0.28.13 h1:wzydkaIekNMuIpeU/ksgxuUFwc0wgEL5Y5hVkPAzni4=
-github.com/gruntwork-io/terratest v0.28.13/go.mod h1:PkVylPuUNmItkfOTwSiFreYA4FkanK8AluBuNeGxQOw=
-github.com/gruntwork-io/terratest v0.28.14 h1:X8AND6VxMdy7vmLiSVwKjuFm83KmWE5s6wssjBQPNuQ=
-github.com/gruntwork-io/terratest v0.28.14/go.mod h1:PkVylPuUNmItkfOTwSiFreYA4FkanK8AluBuNeGxQOw=
-github.com/gruntwork-io/terratest v0.28.15 h1:in1DRBq8/RjxMyb6Amr1SRrczOK/hGnPi+gQXOOtbZI=
-github.com/gruntwork-io/terratest v0.28.15/go.mod h1:PkVylPuUNmItkfOTwSiFreYA4FkanK8AluBuNeGxQOw=
-github.com/gruntwork-io/terratest v0.29.0 h1:EyPLLxglZIHJ0jU1cbx2NJT7A3MVEmPle8ENWDDwVAA=
-github.com/gruntwork-io/terratest v0.29.0/go.mod h1:aVz7181EP4okz7LMx6BLpiF7bL8wkq+h57V6uicvoc0=
-github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
-github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
-github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
-github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
-github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
-github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
-github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
-github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
-github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
-github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
-github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
-github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
-github.com/oracle/oci-go-sdk v7.1.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
-github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.9.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
-github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
-github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
-github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
-github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
-github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
-github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
-github.com/shopspring/decimal v0.0.0-20200105231215-408a2507e114/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
-github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
-github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
-github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
-github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
-github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
-github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
-github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
-github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
-github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
-github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ=
-github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
-github.com/vdemeester/k8s-pkg-credentialprovider v0.0.0-20200107171650-7c61ffa44238/go.mod h1:JwQJCMWpUDqjZrB5jpw0f5VbN7U95zxFy1ZDpoEarGo=
-github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
-go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
-go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
-go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
-golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0=
-golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200109152110-61a87790db17/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
-golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
-golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
-golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
-golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
-golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
-golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
-golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
-golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
-golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
-golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
-golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
-golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191205215504-7b8c8591a921/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200113040837-eac381796e91/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
-gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
-gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
-google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.6.1-0.20190607001116-5213b8090861/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
-google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
-google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.9.1-0.20190821000710-329ecc3c9c34/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
-google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
-google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
-gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
-gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
-gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-k8s.io/api v0.0.0-20181110191121-a33c8200050f/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
-k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI=
-k8s.io/api v0.18.3/go.mod h1:UOaMwERbqJMfeeeHc8XJKawj4P9TgDRnViIqqBeH2QA=
-k8s.io/apimachinery v0.0.0-20190704094520-6f131bee5e2c/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
-k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
-k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
-k8s.io/apiserver v0.17.0/go.mod h1:ABM+9x/prjINN6iiffRVNCBR2Wk7uY4z+EtEGZD48cg=
-k8s.io/client-go v0.0.0-20190704095228-386e588352a4/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
-k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
-k8s.io/client-go v0.18.3/go.mod h1:4a/dpQEvzAhT1BbuWW09qvIaGw6Gbu1gZYiQZIi1DMw=
-k8s.io/cloud-provider v0.17.0/go.mod h1:Ze4c3w2C0bRsjkBUoHpFi+qWe3ob1wI2/7cUn+YQIDE=
-k8s.io/code-generator v0.0.0-20191121015212-c4c8f8345c7e/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
-k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc=
-k8s.io/csi-translation-lib v0.17.0/go.mod h1:HEF7MEz7pOLJCnxabi45IPkhSsE/KmxPQksuCrHKWls=
-k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
-k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
-k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
-k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
-k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
-k8s.io/kubernetes v1.11.10/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
-k8s.io/legacy-cloud-providers v0.17.0/go.mod h1:DdzaepJ3RtRy+e5YhNtrCYwlgyK87j/5+Yfp0L9Syp8=
-k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
-k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
-modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
-modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
-modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
-modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
-modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
-rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
-sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
-sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
-sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
-sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
-sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/tests/no_create/README.md b/tests/no_create/README.md
deleted file mode 100644
index 5119fd8..0000000
--- a/tests/no_create/README.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# Don't Create Resources Test
-
-
-
-## Requirements
-
-| Name | Version |
-|------|---------|
-| terraform | >= 0.12 |
-
-## Providers
-
-No provider.
-
-## Inputs
-
-No input.
-
-## Outputs
-
-No output.
-
-
diff --git a/tests/no_create/main.tf b/tests/no_create/main.tf
index 037eb91..a59a4f5 100644
--- a/tests/no_create/main.tf
+++ b/tests/no_create/main.tf
@@ -9,5 +9,23 @@ module "no_create" {
aws = aws
}
- create_config_rules = false
+ count = 0
+
+ config_recorder_id = null
+ config_rule = {
+ description = null
+ input_parameters = null
+ maximum_execution_frequency = null
+ name = null
+ owner = null
+ scope = null
+ source_details = null
+ source_identifier = null
+ tags = null
+ }
+}
+
+output "no_create" {
+ description = "Module object from no_create"
+ value = module.no_create
}
diff --git a/tests/no_create/versions.tf b/tests/no_create/versions.tf
deleted file mode 100644
index d9b6f79..0000000
--- a/tests/no_create/versions.tf
+++ /dev/null
@@ -1,3 +0,0 @@
-terraform {
- required_version = ">= 0.12"
-}
diff --git a/variables.tf b/variables.tf
index 2abe47b..866f832 100644
--- a/variables.tf
+++ b/variables.tf
@@ -1,41 +1,52 @@
-variable "create_config_rules" {
- description = "Controls whether to create the AWS Config Rules"
- type = bool
- default = true
-}
-
-variable "cloudtrail_bucket" {
- description = "Name of S3 bucket to validate that CloudTrail logs are being delivered"
- type = string
- default = null
-}
-
-variable "config_recorder" {
- description = "The name of the AWS Config recorder"
+variable "config_recorder_id" {
+ description = "ID of the config recorder in the account. Required to address the implicit dependency on the config recorder"
type = string
- default = null
}
-variable "exclude_rules" {
- description = "List of config rule resource names to exclude from creation"
- type = list(string)
- default = []
-}
-
-variable "config_bucket" {
- description = "Name of S3 bucket to validate that Config is configured to send inventory to"
- type = string
- default = null
-}
-
-variable "config_sns_topic_arn" {
- description = "ARN of SNS topic to validate that Config changes are being streamed to"
- type = string
- default = null
+variable "config_rule" {
+ description = "Object of attributes for the config rule resource, see https://www.terraform.io/docs/providers/aws/r/config_config_rule.html#argument-reference. When `owner` is `AWS`, set `source_identifer` to the AWS predefined identifier for the rule. When `owner` is `CUSTOM_LAMBDA`, set `source_identifier` to `null` and it will be set to the ARN of the lambda function"
+ type = object({
+ description = string
+ input_parameters = string
+ maximum_execution_frequency = string
+ name = string
+ owner = string
+ source_identifier = string
+ tags = map(string)
+ scope = object({
+ compliance_resource_id = string
+ compliance_resource_types = list(string)
+ tag_key = string
+ tag_value = string
+ })
+ source_details = list(object({
+ message_type = string
+ }))
+ })
}
-variable "tags" {
- description = "Map of tags to apply to the resources"
- type = map(string)
- default = {}
+variable "lambda" {
+ description = "Object of attributes for the lambda supporting a custom config rule, see https://www.terraform.io/docs/providers/aws/r/lambda_function.html#argument-reference. Required when `config_rule.owner` is `CUSTOM_LAMBDA`"
+ default = {
+ description = null
+ handler = null
+ name = null
+ policy = null
+ runtime = null
+ source_path = null
+ reserved_concurrent_executions = null
+ tags = null
+ timeout = null
+ }
+ type = object({
+ description = string
+ handler = string
+ name = string
+ policy = string
+ runtime = string
+ source_path = string
+ reserved_concurrent_executions = number
+ tags = map(string)
+ timeout = number
+ })
}
diff --git a/vendor/.editorconfig b/vendor/.editorconfig
deleted file mode 100644
index 78b36ca..0000000
--- a/vendor/.editorconfig
+++ /dev/null
@@ -1 +0,0 @@
-root = true
diff --git a/vendor/github.com/awslabs/aws-config-rules/.github/PULL_REQUEST_TEMPLATE.md b/vendor/github.com/awslabs/aws-config-rules/.github/PULL_REQUEST_TEMPLATE.md
deleted file mode 100644
index e80926a..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/.github/PULL_REQUEST_TEMPLATE.md
+++ /dev/null
@@ -1,5 +0,0 @@
-I confirm these files are made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-
-*Issue #, if available:*
-
-*Description of changes:*
diff --git a/vendor/github.com/awslabs/aws-config-rules/.gitignore b/vendor/github.com/awslabs/aws-config-rules/.gitignore
deleted file mode 100644
index b142cd6..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-*.zip
-*.DS_Store
-__pycache__/
-*.pyc
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/LICENSE b/vendor/github.com/awslabs/aws-config-rules/LICENSE
deleted file mode 100644
index 0e259d4..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/LICENSE
+++ /dev/null
@@ -1,121 +0,0 @@
-Creative Commons Legal Code
-
-CC0 1.0 Universal
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
- LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
- ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
- INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
- REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
- PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
- THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
- HEREUNDER.
-
-Statement of Purpose
-
-The laws of most jurisdictions throughout the world automatically confer
-exclusive Copyright and Related Rights (defined below) upon the creator
-and subsequent owner(s) (each and all, an "owner") of an original work of
-authorship and/or a database (each, a "Work").
-
-Certain owners wish to permanently relinquish those rights to a Work for
-the purpose of contributing to a commons of creative, cultural and
-scientific works ("Commons") that the public can reliably and without fear
-of later claims of infringement build upon, modify, incorporate in other
-works, reuse and redistribute as freely as possible in any form whatsoever
-and for any purposes, including without limitation commercial purposes.
-These owners may contribute to the Commons to promote the ideal of a free
-culture and the further production of creative, cultural and scientific
-works, or to gain reputation or greater distribution for their Work in
-part through the use and efforts of others.
-
-For these and/or other purposes and motivations, and without any
-expectation of additional consideration or compensation, the person
-associating CC0 with a Work (the "Affirmer"), to the extent that he or she
-is an owner of Copyright and Related Rights in the Work, voluntarily
-elects to apply CC0 to the Work and publicly distribute the Work under its
-terms, with knowledge of his or her Copyright and Related Rights in the
-Work and the meaning and intended legal effect of CC0 on those rights.
-
-1. Copyright and Related Rights. A Work made available under CC0 may be
-protected by copyright and related or neighboring rights ("Copyright and
-Related Rights"). Copyright and Related Rights include, but are not
-limited to, the following:
-
- i. the right to reproduce, adapt, distribute, perform, display,
- communicate, and translate a Work;
- ii. moral rights retained by the original author(s) and/or performer(s);
-iii. publicity and privacy rights pertaining to a person's image or
- likeness depicted in a Work;
- iv. rights protecting against unfair competition in regards to a Work,
- subject to the limitations in paragraph 4(a), below;
- v. rights protecting the extraction, dissemination, use and reuse of data
- in a Work;
- vi. database rights (such as those arising under Directive 96/9/EC of the
- European Parliament and of the Council of 11 March 1996 on the legal
- protection of databases, and under any national implementation
- thereof, including any amended or successor version of such
- directive); and
-vii. other similar, equivalent or corresponding rights throughout the
- world based on applicable law or treaty, and any national
- implementations thereof.
-
-2. Waiver. To the greatest extent permitted by, but not in contravention
-of, applicable law, Affirmer hereby overtly, fully, permanently,
-irrevocably and unconditionally waives, abandons, and surrenders all of
-Affirmer's Copyright and Related Rights and associated claims and causes
-of action, whether now known or unknown (including existing as well as
-future claims and causes of action), in the Work (i) in all territories
-worldwide, (ii) for the maximum duration provided by applicable law or
-treaty (including future time extensions), (iii) in any current or future
-medium and for any number of copies, and (iv) for any purpose whatsoever,
-including without limitation commercial, advertising or promotional
-purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
-member of the public at large and to the detriment of Affirmer's heirs and
-successors, fully intending that such Waiver shall not be subject to
-revocation, rescission, cancellation, termination, or any other legal or
-equitable action to disrupt the quiet enjoyment of the Work by the public
-as contemplated by Affirmer's express Statement of Purpose.
-
-3. Public License Fallback. Should any part of the Waiver for any reason
-be judged legally invalid or ineffective under applicable law, then the
-Waiver shall be preserved to the maximum extent permitted taking into
-account Affirmer's express Statement of Purpose. In addition, to the
-extent the Waiver is so judged Affirmer hereby grants to each affected
-person a royalty-free, non transferable, non sublicensable, non exclusive,
-irrevocable and unconditional license to exercise Affirmer's Copyright and
-Related Rights in the Work (i) in all territories worldwide, (ii) for the
-maximum duration provided by applicable law or treaty (including future
-time extensions), (iii) in any current or future medium and for any number
-of copies, and (iv) for any purpose whatsoever, including without
-limitation commercial, advertising or promotional purposes (the
-"License"). The License shall be deemed effective as of the date CC0 was
-applied by Affirmer to the Work. Should any part of the License for any
-reason be judged legally invalid or ineffective under applicable law, such
-partial invalidity or ineffectiveness shall not invalidate the remainder
-of the License, and in such case Affirmer hereby affirms that he or she
-will not (i) exercise any of his or her remaining Copyright and Related
-Rights in the Work or (ii) assert any associated claims and causes of
-action with respect to the Work, in either case contrary to Affirmer's
-express Statement of Purpose.
-
-4. Limitations and Disclaimers.
-
- a. No trademark or patent rights held by Affirmer are waived, abandoned,
- surrendered, licensed or otherwise affected by this document.
- b. Affirmer offers the Work as-is and makes no representations or
- warranties of any kind concerning the Work, express, implied,
- statutory or otherwise, including without limitation warranties of
- title, merchantability, fitness for a particular purpose, non
- infringement, or the absence of latent or other defects, accuracy, or
- the present or absence of errors, whether or not discoverable, all to
- the greatest extent permissible under applicable law.
- c. Affirmer disclaims responsibility for clearing rights of other persons
- that may apply to the Work or any use thereof, including without
- limitation any person's Copyright and Related Rights in the Work.
- Further, Affirmer disclaims responsibility for obtaining any necessary
- consents, permissions or other rights required for any use of the
- Work.
- d. Affirmer understands and acknowledges that Creative Commons is not a
- party to this document and has no duty or obligation with respect to
- this CC0 or use of the Work.
diff --git a/vendor/github.com/awslabs/aws-config-rules/README.md b/vendor/github.com/awslabs/aws-config-rules/README.md
deleted file mode 100644
index 5d4081c..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/README.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# AWS Config Rules Repository
-
-AWS Community repository of custom Config rules. Contributions welcome. Instructions for leveraging these rules are below.
-
-**Please review each rule carefully and test within your dev/test environment before integrating into production.**
-
-## Getting started with the developement of Rules
-We recommend to use the RDK (Rule Development Kit) to author Config Rules. It is available here: https://github.com/awslabs/aws-config-rdk
-
-Blog post: https://aws.amazon.com/blogs/mt/how-to-develop-custom-aws-config-rules-using-the-rule-development-kit/
-
-## Related Projects
-RDK (Rule Development Kit) - https://github.com/awslabs/aws-config-rdk
-
-Config Rules Engine (Deploy and manage Rules at scale) - https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-
-## Adding a Rule to AWS Config
-### With the RDK
-In the working folder,
-```
-rdk deploy NAME_OF_THE_RULE
-```
-
-### Manually
-You can use the sample functions in this repository to create Config rules that evaluate the configuration settings of your AWS resources. First, you use AWS Lambda to create a function that is based on the sample code. Then, you use AWS Config to create a rule that is associated with the function. When the rule’s trigger occurs, AWS Config invokes your function to evaluate your AWS resources.
-
-Add a rule to AWS Config by completing the following steps. For more detailed steps, see [Developing a Custom Rule for AWS Config](http://docs.aws.amazon.com/config/latest/developerguide/evaluate-config_develop-rules_nodejs.html) in the *AWS Config Developer Guide*.
-
-1. Navigate to the AWS Lambda Console.
- - Sign in to the AWS Management Console and open the [AWS Lambda console](https://console.aws.amazon.com/lambda/).
- - Verify that your region is set to one that supports AWS Config rules.
- - For the list of supported regions, see [AWS Config Regions and Endpoints](http://docs.aws.amazon.com/general/latest/gr/rande.html#awsconfig_region).
-2. Create a Lambda function.
- - Provide your code using the method required by the code entry type that you choose.
- - If you are adding a Python or Node.js function, you can copy and paste the code from the sample that you want to use. If you are adding a Java function, you must provide a JAR file that contains the Java classes. For instructions to build the JAR file, see [Creating an AWS Config Rule with Java](./java/HOWTO.md).
- - For the role that you assign to your function, choose the **AWS Config Rules permission** option. This includes *AWSConfigRulesExecutionRole*, an AWS managed policy that allows your Lambda function permission to "put" evaluations.
- - For **Handler**, if you are adding a Python or Node.js function, keep the default value. If you are adding a Java function, specify the handler value for to the Java function that you want to use. For the handler values, see [AWS Config Rules (Java)](./java/RULES_JAVA.md).
-3. After you create the function, take note of its ARN.
-4. Open the [AWS Config console](https://console.aws.amazon.com/config/).
- - Verify that your region is set to the same region in which you created the AWS Lambda function for your custom rule.
-5. Use the AWS Config console to add a custom rule.
- - For **AWS Lambda function ARN**, specify the ARN of the function that you created.
- - For **Trigger type**, if you are using any of the *triggered samples* from this repository, choose **Configuration changes**. If you are using any of the *periodic* samples from this repository, choose **Periodic**.
- - For the rule parameters, specify any required parameters.
- - For the trigger types and required parameters for each function, see [AWS Config Rules](./RULES.md) (for Python and Node.js functions) or [AWS Config Rules (Java)](./java/RULES_JAVA.md).
- - **Note**: When you create a custom rule with the AWS Config console, the appropriate permissions for invoking the Lambda are automatically created for you. If you create a custom rule with the AWS CLI, you need to give AWS Config permission to invoke your Lambda function, using the `aws lambda add-permission` command.
-
-After you create the rule, it displays on the **Rules** page, and AWS Config invokes its Lambda function. A summary of the evaluation results appears after several minutes.
diff --git a/vendor/github.com/awslabs/aws-config-rules/java/HOWTO.md b/vendor/github.com/awslabs/aws-config-rules/java/HOWTO.md
deleted file mode 100644
index ed25aad..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/java/HOWTO.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# Creating an AWS Config Rule with Java
-
-You can use any of the sample Java files in this repository to create a custom Config rule. To
-create a Config rule, first you build a JAR file that contains the Java classes. Then, you create an AWS Lambda
-function that uses one of the classes in the JAR. Finally, you create a Config rule that uses the function.
-
-To build the JAR file, you will run a single Apache Maven command. Maven will download the package's
-dependencies, build the package, and test it. To download and install Maven, go to
- .
-
-## Building the JAR File
-
-Run the following Maven command from within the ''java'' directory:
-
-`mvn package`
-
-Maven builds a JAR file and places it in the following path:
-
-'target/aws-config-java-sample-rules-1.0-SNAPSHOT.jar'
-
-## Creating an AWS Lambda Function and AWS Config Rule
-
-For steps to create create a Lambda function and corresponding Config rule, see the [README
-file](../README.md) for the AWS Config Rules repository.
-
-When you use AWS Lambda to create the function, select Java 8 as the runtime. You will need to
-specify the function handler, and you might need to add supplementary permissions to the function's
-execution role. This information is documented in the [list of Java Config rules
-(RULES.md)](RULES_JAVA.md).
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/java/RULES_JAVA.md b/vendor/github.com/awslabs/aws-config-rules/java/RULES_JAVA.md
deleted file mode 100644
index cc874b8..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/java/RULES_JAVA.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# AWS Config Rules (Java)
-This file provides supplementary information for the sample AWS Config Rules in Java.
-
-* **Handler** - The handler value that you provide to AWS Lambda when you create a function.
-* **Supplementary Permissions** - Permissions that you must grant the function's execution role in addition to those that are granted by the AWS Config role.
-* **Trigger Type** - The trigger type that you assign to the Config rule that uses the function.
-* **Required Parameters** - Parameters that are evaluated by the function. You must specify these parameter keys when you create the AWS Config rule.
-
-For the steps to create a Config rule with a Java sample, see the [HOWTO.md](./HOWTO.md) file.
-
-## 1. Ensure MFA Enabled on Root Account
-Description: Checks whether an AWS account is enabled for multi-factor authentication.
-
- \src\main\java\com\amazonaws\services\config\samplerules\RootAccountMFAEnabled.java
-
-* Handler: ```com.amazonaws.services.config.samplerules.RootAccountMFAEnabled::handle```
-* Supplementary Permissions: ```iam:GetAccountSummary```
-* Trigger Type: ```Periodic```
-* Required Parameters: ```None```
diff --git a/vendor/github.com/awslabs/aws-config-rules/java/pom.xml b/vendor/github.com/awslabs/aws-config-rules/java/pom.xml
deleted file mode 100644
index f2da0b8..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/java/pom.xml
+++ /dev/null
@@ -1,90 +0,0 @@
-
- 4.0.0
-
- com.amazonaws
- aws-config-java-sample-rules
- jar
- 1.0-SNAPSHOT
- AWS Config Sample Rule Library
-
-
-
- com.amazonaws
- aws-lambda-java-core
- 1.1.0
-
-
- com.amazonaws
- aws-lambda-java-events
- 1.2.0
-
-
- com.amazonaws
- aws-java-sdk-config
- [1.10.5,)
-
-
- com.amazonaws
- aws-java-sdk-iam
- [1.10.5,)
-
-
- com.fasterxml.jackson.core
- jackson-core
- 2.7.0
-
-
- junit
- junit
- 4.11
- test
-
-
- org.mockito
- mockito-core
- 1.9.5
- test
-
-
- org.apache.commons
- commons-lang3
- 3.0
-
-
- com.google.guava
- guava
- 18.0
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
- 1.8
- 1.8
-
-
-
- org.apache.maven.plugins
- maven-shade-plugin
- 2.3
-
- false
-
-
-
- package
-
- shade
-
-
-
-
-
-
-
diff --git a/vendor/github.com/awslabs/aws-config-rules/java/src/main/java/com/amazonaws/services/config/samplerules/DbInstanceBackupParameters.java b/vendor/github.com/awslabs/aws-config-rules/java/src/main/java/com/amazonaws/services/config/samplerules/DbInstanceBackupParameters.java
deleted file mode 100644
index 459bf92..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/java/src/main/java/com/amazonaws/services/config/samplerules/DbInstanceBackupParameters.java
+++ /dev/null
@@ -1,179 +0,0 @@
-package com.amazonaws.services.config.samplerules;
-
-import java.io.IOException;
-import java.time.Instant;
-import java.time.format.DateTimeFormatter;
-import java.util.Date;
-
-import org.apache.commons.lang3.StringUtils;
-
-import com.amazonaws.regions.Regions;
-import com.amazonaws.services.config.AmazonConfig;
-import com.amazonaws.services.config.AmazonConfigClient;
-import com.amazonaws.services.config.model.*;
-import com.amazonaws.services.config.samplerules.exception.FunctionExecutionException;
-import com.amazonaws.services.lambda.runtime.Context;
-import com.amazonaws.services.lambda.runtime.events.ConfigEvent;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.annotations.VisibleForTesting;
-
-public class DbInstanceBackupParameters {
-
- private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
- private static final String AWS_REGION_PROPERTY = "AWS_DEFAULT_REGION";
- private static final String MESSAGE_TYPE_PROPERTY = "messageType";
- private static final String CAPTURE_TIME_PATH = "configurationItemCaptureTime";
- private static final String STATUS_PATH = "configurationItemStatus";
- private static final String RESOURCE_DELETED = "ResourceDeleted";
- private static final String RESOURCE_DELETED_NOT_RECORDED = "ResourceDeletedNotRecorded";
- private static final String RESOURCE_NOT_RECORDED = "ResourceNotRecorded";
- private static final String CONFIGURATION = "configuration";
- private static final String RETENTION_PERIOD = "backupRetentionPeriod";
- private static final String BACKUP_WINDOW = "preferredBackupWindow";
- private static final String CONFIGURATION_ITEM = "configurationItem";
- private static final String RESOURCE_ID = "resourceId";
- private static final String RESOURCE_TYPE = "resourceType";
-
- @VisibleForTesting
- protected static final String INCOMPATIBLE_EVENT_TYPES_MESSAGE = "Events with the message type '%s' are not evaluated for this Config rule.";
- @VisibleForTesting
- protected static final String INVALID_BACKUP_RETENTION_PERIOD = "BackupRetentionPeriod rule parameter value '%s' is not an integer";
-
- /**
- * This handler function is executed when AWS Lambda passes the event and context objects.
- *
- * @param event
- * Event object published by AWS Config to invoke the function.
- * @param context
- * Context object provided by AWS Lambda.
- * @throws IOException
- */
- public void handle(ConfigEvent event, Context context) throws IOException {
- Regions region = Regions.fromName(System.getenv(AWS_REGION_PROPERTY));
- AmazonConfig configClient = new AmazonConfigClient()
- .withRegion(region);
- doHandle(event, context, configClient);
- }
-
- /**
- * Handler interface used by the main handler function and test events.
- */
- @VisibleForTesting
- void doHandle(ConfigEvent event, Context context, AmazonConfig configClient) throws IOException {
- JsonNode invokingEvent = OBJECT_MAPPER.readTree(event.getInvokingEvent());
- failForIncompatibleEventTypes(invokingEvent);
-
- // Associates the evaluation result with the AWS account published in the event.
- Evaluation evaluation = new Evaluation()
- .withComplianceResourceId(getResourceId(invokingEvent))
- .withComplianceResourceType(getResourceType(invokingEvent))
- .withOrderingTimestamp(getCiCapturedTime(invokingEvent))
- .withComplianceType(evaluateCompliance(event));
- doPutEvaluations(configClient, event, evaluation);
- }
-
- private void failForIncompatibleEventTypes(JsonNode invokingEvent) {
- String messageType = invokingEvent.path(MESSAGE_TYPE_PROPERTY).textValue();
- if (!isCompatibleMessageType(messageType)) {
- throw new FunctionExecutionException(String.format(INCOMPATIBLE_EVENT_TYPES_MESSAGE, messageType));
- }
- }
-
- private boolean isCompatibleMessageType(String messageType) {
- return MessageType.ConfigurationItemChangeNotification.toString().equals(messageType);
- }
-
- private String getResourceId(JsonNode invokingEvent) {
- return invokingEvent.path(CONFIGURATION_ITEM).path(RESOURCE_ID).textValue();
- }
-
- private String getResourceType(JsonNode invokingEvent) {
- return invokingEvent.path(CONFIGURATION_ITEM).path(RESOURCE_TYPE).textValue();
- }
-
- private Date getCiCapturedTime(JsonNode invokingEvent) {
- return getDate(invokingEvent.path(CONFIGURATION_ITEM).path(CAPTURE_TIME_PATH).textValue());
- }
-
- private Date getDate(String dateString) {
- return Date.from(Instant.from(DateTimeFormatter.ISO_INSTANT.parse(dateString)));
- }
-
- private ComplianceType evaluateCompliance(ConfigEvent event) throws JsonProcessingException, IOException {
- JsonNode invokingEvent = OBJECT_MAPPER.readTree(event.getInvokingEvent());
- JsonNode ruleParameters = OBJECT_MAPPER.readTree(event.getRuleParameters());
- if (isEventNotApplicable(invokingEvent, event.isEventLeftScope()) || isNotDBInstance(invokingEvent)) {
- return ComplianceType.NOT_APPLICABLE;
- } else if (isBackUpTurnedOn(invokingEvent) && isExpectedBackUpWindow(invokingEvent, ruleParameters)
- && isExpectedRetentionPeriod(invokingEvent, ruleParameters))
- {
- return ComplianceType.COMPLIANT;
- } else {
- return ComplianceType.NON_COMPLIANT;
- }
- }
-
- private boolean isBackUpTurnedOn(JsonNode invokingEvent) {
- return invokingEvent.path(CONFIGURATION_ITEM).path(CONFIGURATION).path(RETENTION_PERIOD).intValue() > 0;
- }
-
- private boolean isExpectedBackUpWindow(JsonNode invokingEvent, JsonNode ruleParameters) {
- String expectedBackupWindow = ruleParameters.path(BACKUP_WINDOW).textValue();
- String actualBackupWindow = invokingEvent.path(CONFIGURATION_ITEM).path(CONFIGURATION).path(BACKUP_WINDOW)
- .textValue();
- return StringUtils.isBlank(expectedBackupWindow) ? true : StringUtils.equalsIgnoreCase(
- expectedBackupWindow, actualBackupWindow);
- }
-
- private boolean isExpectedRetentionPeriod(JsonNode invokingEvent, JsonNode ruleParameters) {
- String expectedRetentionPeriodString = ruleParameters.path(RETENTION_PERIOD).textValue();
- if (StringUtils.isBlank(expectedRetentionPeriodString)) {
- return true;
- } else {
- int expectedRetentionPeriodInteger = tryGetRetentionPeriodInteger(expectedRetentionPeriodString);
- int actualRetentionPeriod = invokingEvent.path(CONFIGURATION_ITEM).path(CONFIGURATION)
- .path(RETENTION_PERIOD).intValue();
- return expectedRetentionPeriodInteger == actualRetentionPeriod;
- }
- }
-
- private int tryGetRetentionPeriodInteger(String retentionPeriodString) {
- if (StringUtils.isNumeric(retentionPeriodString)) {
- return Integer.parseInt(retentionPeriodString);
- }
- else {
- throw new FunctionExecutionException(String.format(INVALID_BACKUP_RETENTION_PERIOD, retentionPeriodString));
- }
- }
-
- private boolean isEventNotApplicable(JsonNode invokingEvent, boolean eventLeftScope) {
- String status = invokingEvent.path(CONFIGURATION_ITEM).path(STATUS_PATH).textValue();
- return (isStatusNotApplicable(status) || eventLeftScope);
- }
-
- private boolean isNotDBInstance(JsonNode invokingEvent) {
- return !ResourceType.AWSRDSDBInstance.toString().equals(
- invokingEvent.path(CONFIGURATION_ITEM).path(RESOURCE_TYPE).textValue());
- }
-
- private boolean isStatusNotApplicable(String status) {
- return RESOURCE_DELETED.equals(status) || RESOURCE_DELETED_NOT_RECORDED.equals(status)
- || RESOURCE_NOT_RECORDED.equals(status);
- }
-
- // Sends the evaluation results to AWS Config.
- private void doPutEvaluations(AmazonConfig configClient, ConfigEvent event, Evaluation evaluation) {
- PutEvaluationsResult result = configClient.putEvaluations(new PutEvaluationsRequest()
- .withEvaluations(evaluation)
- .withResultToken(event.getResultToken()));
- // Ends the function execution if any evaluation results are not successfully reported.
- if (result.getFailedEvaluations().size() > 0) {
- throw new FunctionExecutionException(String.format(
- "The following evaluations were not successfully reported to AWS Config: %s",
- result.getFailedEvaluations()));
- }
- }
-}
-
diff --git a/vendor/github.com/awslabs/aws-config-rules/java/src/main/java/com/amazonaws/services/config/samplerules/DesiredInstanceTenancy.java b/vendor/github.com/awslabs/aws-config-rules/java/src/main/java/com/amazonaws/services/config/samplerules/DesiredInstanceTenancy.java
deleted file mode 100644
index f1f981d..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/java/src/main/java/com/amazonaws/services/config/samplerules/DesiredInstanceTenancy.java
+++ /dev/null
@@ -1,162 +0,0 @@
-package com.amazonaws.services.config.samplerules;
-
-import java.io.IOException;
-import java.time.Instant;
-import java.time.format.DateTimeFormatter;
-import java.util.Date;
-
-import org.apache.commons.lang3.StringUtils;
-
-import com.amazonaws.regions.Regions;
-import com.amazonaws.services.config.AmazonConfig;
-import com.amazonaws.services.config.AmazonConfigClient;
-import com.amazonaws.services.config.model.*;
-import com.amazonaws.services.config.samplerules.exception.FunctionExecutionException;
-import com.amazonaws.services.lambda.runtime.Context;
-import com.amazonaws.services.lambda.runtime.events.ConfigEvent;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-public class DesiredInstanceTenancy {
-
- private static final String AWS_REGION_PROPERTY = "AWS_DEFAULT_REGION";
- private static final String MESSAGE_TYPE_PROPERTY = "messageType";
- private static final String HOST_ID = "hostId";
- private static final String PLACEMENT = "placement";
- private static final String CONFIGURATION = "configuration";
- private static final String IMAGE_ID = "imageId";
- private static final String STATUS_PATH = "configurationItemStatus";
- private static final String TENANCY = "tenancy";
- private static final String RESOURCE_DELETED = "ResourceDeleted";
- private static final String RESOURCE_DELETED_NOT_RECORDED = "ResourceDeletedNotRecorded";
- private static final String CAPTURE_TIME_PATH = "configurationItemCaptureTime";
- private static final String CONFIGURATION_ITEM = "configurationItem";
- private static final String RESOURCE_ID = "resourceId";
- private static final Object RESOURCE_NOT_RECORDED = "ResourceNotRecorded";
- private static final String RESOURCE_TYPE = "resourceType";
-
- /**
- * This handler function is executed when AWS Lambda passes the event and context objects.
- *
- * @param event
- * Event object published by AWS Config to invoke the function.
- * @param context
- * Context object provided by AWS Lambda.
- * @throws IOException
- */
- public void handle(ConfigEvent event, Context context) throws IOException {
- Regions region = Regions.fromName(System.getenv(AWS_REGION_PROPERTY));
- AmazonConfig configClient = new AmazonConfigClient()
- .withRegion(region);
- doHandle(event, context, configClient);
- }
-
- /**
- * Handler interface used by the main handler function and test events.
- */
- public void doHandle(ConfigEvent event, Context context, AmazonConfig configClient) throws IOException {
- JsonNode invokingEvent = new ObjectMapper().readTree(event.getInvokingEvent());
- failForIncompatibleEventTypes(invokingEvent);
-
- // Associates the evaluation result with the AWS account published in the event.
- Evaluation evaluation = new Evaluation()
- .withComplianceResourceId(getResourceId(invokingEvent))
- .withComplianceResourceType(getResourceType(invokingEvent))
- .withOrderingTimestamp(getCiCapturedTime(invokingEvent))
- .withComplianceType(evaluateCompliance(event));
- doPutEvaluations(configClient, event, evaluation);
- }
-
- private String getResourceType(JsonNode invokingEvent) {
- return invokingEvent.path(CONFIGURATION_ITEM).path(RESOURCE_TYPE).textValue();
- }
-
- private void failForIncompatibleEventTypes(JsonNode invokingEvent) {
- String messageType = invokingEvent.path(MESSAGE_TYPE_PROPERTY).textValue();
- if (!isCompatibleMessageType(messageType)) {
- throw new FunctionExecutionException(String.format(
- "Events with the message type '%s' are not evaluated for this Config rule.", messageType));
- }
- }
-
- private String getResourceId(JsonNode invokingEvent) {
- return invokingEvent.path(CONFIGURATION_ITEM).path(RESOURCE_ID).textValue();
- }
-
- private Date getCiCapturedTime(JsonNode invokingEvent) {
- return getDate(invokingEvent.path(CONFIGURATION_ITEM).path(CAPTURE_TIME_PATH).textValue());
- }
-
- private ComplianceType evaluateCompliance(ConfigEvent event) throws JsonProcessingException,
- IOException {
- JsonNode invokingEvent = new ObjectMapper().readTree(event.getInvokingEvent());
- JsonNode ruleParameters = new ObjectMapper().readTree(event.getRuleParameters());
-
- if (isEventNotApplicable(invokingEvent, event.isEventLeftScope())
- || !hasExpectedImageId(invokingEvent, ruleParameters))
- {
- return ComplianceType.NOT_APPLICABLE;
- } else if (isDesiredTenancy(invokingEvent, ruleParameters)
- && isOnExpectedDedicatedHost(invokingEvent, ruleParameters))
- {
- return ComplianceType.COMPLIANT;
- } else {
- return ComplianceType.NON_COMPLIANT;
- }
- }
-
- private boolean isCompatibleMessageType(String messageType) {
- return MessageType.ConfigurationItemChangeNotification.toString().equals(messageType);
- }
-
- private boolean isEventNotApplicable(JsonNode invokingEvent, boolean eventLeftScope) {
- String status = invokingEvent.path(CONFIGURATION_ITEM).path(STATUS_PATH).textValue();
- return (isStatusNotApplicable(status) || eventLeftScope);
- }
-
- private boolean isStatusNotApplicable(String status) {
- return RESOURCE_DELETED.equals(status) || RESOURCE_DELETED_NOT_RECORDED.equals(status)
- || RESOURCE_NOT_RECORDED.equals(status);
- }
-
- private boolean isDesiredTenancy(JsonNode invokingEvent, JsonNode ruleParameters) {
- String expectedTenancy = ruleParameters.path(TENANCY).textValue();
- String actualTenancy = invokingEvent.path(CONFIGURATION_ITEM).path(CONFIGURATION).path(PLACEMENT).path(TENANCY)
- .textValue();
- return StringUtils.equalsIgnoreCase(expectedTenancy, actualTenancy);
- }
-
- private boolean hasExpectedImageId(JsonNode invokingEvent, JsonNode ruleParameters) throws JsonProcessingException,
- IOException {
- String expectedImageId = ruleParameters.path(IMAGE_ID).textValue();
- String actualImageId = invokingEvent.path(CONFIGURATION_ITEM).path(CONFIGURATION).path(IMAGE_ID).textValue();
- return StringUtils.isBlank(expectedImageId) ? true : StringUtils.equalsIgnoreCase(expectedImageId,
- actualImageId);
- }
-
- private boolean isOnExpectedDedicatedHost(JsonNode invokingEvent, JsonNode ruleParameters)
- throws JsonProcessingException, IOException {
- String expectedHostId = ruleParameters.path(HOST_ID).textValue();
- String actualHostId = invokingEvent.path(CONFIGURATION_ITEM).path(CONFIGURATION).path(PLACEMENT).path(HOST_ID)
- .textValue();
- return StringUtils.isBlank(expectedHostId) ? true : StringUtils.equalsIgnoreCase(expectedHostId, actualHostId);
- }
-
- // Sends the evaluation results to AWS Config.
- private void doPutEvaluations(AmazonConfig configClient, ConfigEvent event, Evaluation evaluation) {
- PutEvaluationsResult result = configClient.putEvaluations(new PutEvaluationsRequest()
- .withEvaluations(evaluation)
- .withResultToken(event.getResultToken()));
- // Ends the function execution if any evaluation results are not successfully reported.
- if (result.getFailedEvaluations().size() > 0) {
- throw new FunctionExecutionException(String.format(
- "The following evaluations were not successfully reported to AWS Config: %s",
- result.getFailedEvaluations()));
- }
- }
-
- private Date getDate(String dateString) {
- return Date.from(Instant.from(DateTimeFormatter.ISO_INSTANT.parse(dateString)));
- }
-}
diff --git a/vendor/github.com/awslabs/aws-config-rules/java/src/main/java/com/amazonaws/services/config/samplerules/RootAccountMFAEnabled.java b/vendor/github.com/awslabs/aws-config-rules/java/src/main/java/com/amazonaws/services/config/samplerules/RootAccountMFAEnabled.java
deleted file mode 100644
index 1a45581..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/java/src/main/java/com/amazonaws/services/config/samplerules/RootAccountMFAEnabled.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package com.amazonaws.services.config.samplerules;
-
-import java.io.IOException;
-import java.util.Date;
-import java.util.function.Supplier;
-
-import com.amazonaws.regions.Regions;
-import com.amazonaws.services.config.AmazonConfig;
-import com.amazonaws.services.config.AmazonConfigClient;
-import com.amazonaws.services.config.model.*;
-import com.amazonaws.services.config.samplerules.exception.FunctionExecutionException;
-import com.amazonaws.services.identitymanagement.AmazonIdentityManagement;
-import com.amazonaws.services.identitymanagement.AmazonIdentityManagementClient;
-import com.amazonaws.services.identitymanagement.model.GetAccountSummaryResult;
-import com.amazonaws.services.lambda.runtime.Context;
-import com.amazonaws.services.lambda.runtime.events.ConfigEvent;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-/**
- * This Lambda function reports to AWS Config whether an AWS account is enabled for Multi-Factor Authentication. The
- * function is invoked when AWS Config publishes an event for periodic Config rules.
- */
-public class RootAccountMFAEnabled {
-
- private static final String AWS_ACCOUNT_RESOURCE_TYPE = "AWS::::Account";
- private static final String AWS_REGION_PROPERTY = "AWS_DEFAULT_REGION";
- private static final String MESSAGE_TYPE_PROPERTY = "messageType";
- private static final String MFA_ENABLED_PROPERTY = "AccountMFAEnabled";
-
- /**
- * This handler function is executed when AWS Lambda passes the event and context objects.
- *
- * @param event
- * Event object published by AWS Config to invoke the function.
- * @param context
- * Context object provided by AWS Lambda.
- * @throws IOException
- */
- public void handle(ConfigEvent event, Context context) throws IOException {
- Regions region = Regions.fromName(System.getenv(AWS_REGION_PROPERTY));
- AmazonConfig configClient = new AmazonConfigClient()
- .withRegion(region);
- AmazonIdentityManagement iamClient = new AmazonIdentityManagementClient()
- .withRegion(region);
- doHandle(event, context, configClient, iamClient, Date::new);
- }
-
- /**
- * Handler interface used by the main handler function and test events.
- */
- public void doHandle(ConfigEvent event, Context context, AmazonConfig configClient,
- AmazonIdentityManagement iamClient, Supplier dateSupplier) throws IOException {
- JsonNode invokingEvent = new ObjectMapper().readTree(event.getInvokingEvent());
- failForIncompatibleEventTypes(invokingEvent);
- // Associates the evaluation result with the AWS account published in the event.
- Evaluation evaluation = new Evaluation()
- .withComplianceResourceId(event.getAccountId())
- .withComplianceResourceType(AWS_ACCOUNT_RESOURCE_TYPE)
- .withOrderingTimestamp(dateSupplier.get())
- .withComplianceType(getCompliance(iamClient));
- doPutEvaluations(configClient, event, evaluation);
- }
-
- // Ends the function execution if the event is not meant for periodic evaluations.
- private void failForIncompatibleEventTypes(JsonNode invokingEvent) {
- String messageType = invokingEvent.get(MESSAGE_TYPE_PROPERTY).asText();
- if (!MessageType.ScheduledNotification.toString().equals(messageType)) {
- throw new FunctionExecutionException(String.format(
- "Events with the message type '%s' are not evaluated for this Config rule.", messageType));
- }
- }
-
- // Evaluates whether the AWS Account published in the event has an MFA device assigned.
- private ComplianceType getCompliance(AmazonIdentityManagement iamClient) {
- GetAccountSummaryResult result = iamClient.getAccountSummary();
- Integer mfaEnabledCount = result.getSummaryMap().get(MFA_ENABLED_PROPERTY);
- if (mfaEnabledCount != null && mfaEnabledCount > 0) {
- return ComplianceType.COMPLIANT; // The account has an MFA device assigned.
- } else {
- return ComplianceType.NON_COMPLIANT; // The account does not have an MFA device assigned.
- }
- }
-
- // Sends the evaluation results to AWS Config.
- private void doPutEvaluations(AmazonConfig configClient, ConfigEvent event, Evaluation evaluation) {
- PutEvaluationsResult result = configClient.putEvaluations(new PutEvaluationsRequest()
- .withEvaluations(evaluation)
- .withResultToken(event.getResultToken()));
- // Ends the function execution if any evaluation results are not successfully reported.
- if (result.getFailedEvaluations().size() > 0) {
- throw new FunctionExecutionException(String.format(
- "The following evaluations were not successfully reported to AWS Config: %s",
- result.getFailedEvaluations()));
- }
- }
-
-}
diff --git a/vendor/github.com/awslabs/aws-config-rules/java/src/main/java/com/amazonaws/services/config/samplerules/exception/FunctionExecutionException.java b/vendor/github.com/awslabs/aws-config-rules/java/src/main/java/com/amazonaws/services/config/samplerules/exception/FunctionExecutionException.java
deleted file mode 100644
index 345dd3e..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/java/src/main/java/com/amazonaws/services/config/samplerules/exception/FunctionExecutionException.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.amazonaws.services.config.samplerules.exception;
-
-public class FunctionExecutionException extends RuntimeException {
-
- private static final long serialVersionUID = 1L;
-
- public FunctionExecutionException(String message) {
- super(message);
- }
-}
diff --git a/vendor/github.com/awslabs/aws-config-rules/java/src/test/java/com/amazonaws/services/config/samplerules/DbInstanceBackupParametersTest.java b/vendor/github.com/awslabs/aws-config-rules/java/src/test/java/com/amazonaws/services/config/samplerules/DbInstanceBackupParametersTest.java
deleted file mode 100644
index ab1fc37..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/java/src/test/java/com/amazonaws/services/config/samplerules/DbInstanceBackupParametersTest.java
+++ /dev/null
@@ -1,259 +0,0 @@
-package com.amazonaws.services.config.samplerules;
-
-import static org.junit.Assert.*;
-import static org.mockito.Matchers.*;
-import static org.mockito.Mockito.*;
-
-import java.io.IOException;
-import java.time.Instant;
-import java.time.format.DateTimeFormatter;
-import java.util.Collections;
-import java.util.Date;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.runners.MockitoJUnitRunner;
-
-import com.amazonaws.services.config.AmazonConfig;
-import com.amazonaws.services.config.model.*;
-import com.amazonaws.services.config.samplerules.exception.FunctionExecutionException;
-import com.amazonaws.services.lambda.runtime.Context;
-import com.amazonaws.services.lambda.runtime.events.ConfigEvent;
-
-@RunWith(MockitoJUnitRunner.class)
-public class DbInstanceBackupParametersTest {
-
- private static final String ACCOUNT_ID = "accountId";
- private static final String RESULT_TOKEN = "resultToken";
- private static final String PARAMETERS_FORMAT =
- "{\"preferredBackupWindow\": \"%s\",\"backupRetentionPeriod\":\"%s\"}";
- private static final String BACKUPWINDOW_ONLY_FORMAT = "{\"preferredBackupWindow\": \"%s\"}";
- private static final String RETENTION_PERIOD_ONLY_FORMAT = "{\"backupRetentionPeriod\":\"%s\"}";
- private static final String ACTUAL_BACKUP_WINDOW = "actualBackupWindow";
- private static final String ACTUAL_RENTENTION_PERIOD = "7";
- private static final String WRONG_RENTENTION_PERIOD = "10";
- private static final String INVALID_RETENTION_PERIOD = "test";
- private static final String NO_PARAMETERS = "{}";
- private static final String ZERO_RENTENTION_PERIOD = "0";
- private static final String WRONG_BACKUP_WINDOW = "wrongBackupWindow";
- //@formatter:off
- private static final String EC2_INSTANCE_CONFIG_EVENT =
- "{"
- + "\"configurationItem\":{"
- + "\"configurationItemCaptureTime\":\"%s\","
- + "\"configurationItemStatus\":\"%s\","
- + "\"resourceType\":\"AWS::EC2::Instance\","
- + "\"resourceId\":\"%s\""
- + "},"
- + "\"messageType\":\"%s\""
- + "}";
- //@formatter:on
-
- @Mock
- private Context context;
- @Mock
- private AmazonConfig configClient;
-
- private String configurationItemStatus;
- private ConfigEvent event;
- private DbInstanceBackupParameters dbInstanceBackupParameters;
-
- @Before
- public void setup() {
- configurationItemStatus = "OK";
- event = new ConfigEvent();
- event.setAccountId(ACCOUNT_ID);
- event.setResultToken(RESULT_TOKEN);
- event.setEventLeftScope(false);
- dbInstanceBackupParameters = new DbInstanceBackupParameters();
- when(configClient.putEvaluations(any(PutEvaluationsRequest.class)))
- .thenReturn(new PutEvaluationsResult().withFailedEvaluations(Collections.emptyList()));
- }
-
- @Test
- public void testCompliant_withAllParams() throws IOException {
- event.setInvokingEvent(DbInstanceConfigEventBuilder.buildInvokingEvent(ACTUAL_BACKUP_WINDOW,
- ACTUAL_RENTENTION_PERIOD, configurationItemStatus, MessageType.ConfigurationItemChangeNotification));
- event.setRuleParameters(String.format(PARAMETERS_FORMAT, ACTUAL_BACKUP_WINDOW, ACTUAL_RENTENTION_PERIOD));
- invokeAndAssertRuleCompliance(ComplianceType.COMPLIANT, ResourceType.AWSRDSDBInstance);
- }
-
- @Test
- public void testNonCompliant_forAllParms() throws IOException {
- event.setInvokingEvent(DbInstanceConfigEventBuilder.buildInvokingEvent(WRONG_BACKUP_WINDOW,
- WRONG_RENTENTION_PERIOD, configurationItemStatus, MessageType.ConfigurationItemChangeNotification));
- event.setRuleParameters(String.format(PARAMETERS_FORMAT, ACTUAL_BACKUP_WINDOW, ACTUAL_RENTENTION_PERIOD));
- invokeAndAssertRuleCompliance(ComplianceType.NON_COMPLIANT, ResourceType.AWSRDSDBInstance);
- }
-
- @Test
- public void testCompliant_withNoParams() throws IOException {
- event.setInvokingEvent(DbInstanceConfigEventBuilder.buildInvokingEvent(ACTUAL_BACKUP_WINDOW,
- ACTUAL_RENTENTION_PERIOD, configurationItemStatus, MessageType.ConfigurationItemChangeNotification));
- event.setRuleParameters(NO_PARAMETERS);
- invokeAndAssertRuleCompliance(ComplianceType.COMPLIANT, ResourceType.AWSRDSDBInstance);
- }
-
- @Test
- public void testNonCompliant_withNoParams() throws IOException {
- event.setInvokingEvent(DbInstanceConfigEventBuilder.buildInvokingEvent(ACTUAL_BACKUP_WINDOW,
- ZERO_RENTENTION_PERIOD, configurationItemStatus, MessageType.ConfigurationItemChangeNotification));
- event.setRuleParameters(NO_PARAMETERS);
- invokeAndAssertRuleCompliance(ComplianceType.NON_COMPLIANT, ResourceType.AWSRDSDBInstance);
- }
-
- @Test
- public void testNonCompliant_withRetentionPeriodOnly() throws IOException {
- event.setInvokingEvent(DbInstanceConfigEventBuilder.buildInvokingEvent(ACTUAL_BACKUP_WINDOW,
- WRONG_RENTENTION_PERIOD, configurationItemStatus, MessageType.ConfigurationItemChangeNotification));
- event.setRuleParameters(String.format(RETENTION_PERIOD_ONLY_FORMAT, ACTUAL_RENTENTION_PERIOD));
- invokeAndAssertRuleCompliance(ComplianceType.NON_COMPLIANT, ResourceType.AWSRDSDBInstance);
- }
-
- @Test
- public void testCompliant_withRetentionPeriodOnly() throws IOException {
- event.setInvokingEvent(DbInstanceConfigEventBuilder.buildInvokingEvent(ACTUAL_BACKUP_WINDOW,
- ACTUAL_RENTENTION_PERIOD, configurationItemStatus, MessageType.ConfigurationItemChangeNotification));
- event.setRuleParameters(String.format(RETENTION_PERIOD_ONLY_FORMAT, ACTUAL_RENTENTION_PERIOD));
- invokeAndAssertRuleCompliance(ComplianceType.COMPLIANT, ResourceType.AWSRDSDBInstance);
- }
-
- @Test
- public void testException_invalidRententionPeriod() throws IOException {
- event.setInvokingEvent(DbInstanceConfigEventBuilder.buildInvokingEvent(ACTUAL_BACKUP_WINDOW,
- ACTUAL_RENTENTION_PERIOD, configurationItemStatus, MessageType.ConfigurationItemChangeNotification));
- event.setRuleParameters(String.format(PARAMETERS_FORMAT, ACTUAL_BACKUP_WINDOW, INVALID_RETENTION_PERIOD));
- invokeAndAssertException(String.format(DbInstanceBackupParameters.INVALID_BACKUP_RETENTION_PERIOD,
- INVALID_RETENTION_PERIOD));
- }
-
- @Test
- public void testCompliant_withBackupWindowOnly() throws IOException {
- event.setInvokingEvent(DbInstanceConfigEventBuilder.buildInvokingEvent(ACTUAL_BACKUP_WINDOW,
- ACTUAL_RENTENTION_PERIOD, configurationItemStatus, MessageType.ConfigurationItemChangeNotification));
- event.setRuleParameters(String.format(BACKUPWINDOW_ONLY_FORMAT, ACTUAL_BACKUP_WINDOW));
- invokeAndAssertRuleCompliance(ComplianceType.COMPLIANT, ResourceType.AWSRDSDBInstance);
- }
-
- @Test
- public void testNonCompliant_withBackupWindowOnly() throws IOException {
- event.setInvokingEvent(DbInstanceConfigEventBuilder.buildInvokingEvent(WRONG_BACKUP_WINDOW,
- ACTUAL_RENTENTION_PERIOD, configurationItemStatus, MessageType.ConfigurationItemChangeNotification));
- event.setRuleParameters(String.format(BACKUPWINDOW_ONLY_FORMAT, ACTUAL_BACKUP_WINDOW));
- invokeAndAssertRuleCompliance(ComplianceType.NON_COMPLIANT, ResourceType.AWSRDSDBInstance);
- }
-
- @Test
- public void testNotApplicable_deletedResource() throws IOException {
- configurationItemStatus = "ResourceDeleted";
- event.setInvokingEvent(DbInstanceConfigEventBuilder.buildInvokingEvent(ACTUAL_BACKUP_WINDOW,
- ACTUAL_RENTENTION_PERIOD, configurationItemStatus, MessageType.ConfigurationItemChangeNotification));
- event.setRuleParameters(String.format(PARAMETERS_FORMAT, ACTUAL_BACKUP_WINDOW, ACTUAL_RENTENTION_PERIOD));
- invokeAndAssertRuleCompliance(ComplianceType.NOT_APPLICABLE, ResourceType.AWSRDSDBInstance);
- }
-
- @Test
- public void testNotApplicable_deletedNotRecordedResource() throws IOException {
- configurationItemStatus = "ResourceDeletedNotRecorded";
- event.setInvokingEvent(DbInstanceConfigEventBuilder.buildInvokingEvent(ACTUAL_BACKUP_WINDOW,
- ACTUAL_RENTENTION_PERIOD, configurationItemStatus, MessageType.ConfigurationItemChangeNotification));
- event.setRuleParameters(String.format(PARAMETERS_FORMAT, ACTUAL_BACKUP_WINDOW, ACTUAL_RENTENTION_PERIOD));
- invokeAndAssertRuleCompliance(ComplianceType.NOT_APPLICABLE, ResourceType.AWSRDSDBInstance);
- }
-
- @Test
- public void testNotApplicable_notRecordedResource() throws IOException {
- configurationItemStatus = "ResourceNotRecorded";
- event.setInvokingEvent(DbInstanceConfigEventBuilder.buildInvokingEvent(ACTUAL_BACKUP_WINDOW,
- ACTUAL_RENTENTION_PERIOD, configurationItemStatus, MessageType.ConfigurationItemChangeNotification));
- event.setRuleParameters(String.format(PARAMETERS_FORMAT, ACTUAL_BACKUP_WINDOW, ACTUAL_RENTENTION_PERIOD));
- invokeAndAssertRuleCompliance(ComplianceType.NOT_APPLICABLE, ResourceType.AWSRDSDBInstance);
- }
-
- @Test
- public void testNotApplicable_eventLeftScope() throws IOException {
- event.setEventLeftScope(true);
- event.setInvokingEvent(DbInstanceConfigEventBuilder.buildInvokingEvent(ACTUAL_BACKUP_WINDOW,
- ACTUAL_RENTENTION_PERIOD, configurationItemStatus, MessageType.ConfigurationItemChangeNotification));
- event.setRuleParameters(String.format(PARAMETERS_FORMAT, ACTUAL_BACKUP_WINDOW, ACTUAL_RENTENTION_PERIOD));
- invokeAndAssertRuleCompliance(ComplianceType.NOT_APPLICABLE, ResourceType.AWSRDSDBInstance);
- }
-
- @Test
- public void testNotApplicable_nonDBInstance() throws IOException {
- event.setInvokingEvent(String.format(EC2_INSTANCE_CONFIG_EVENT, DbInstanceConfigEventBuilder.CI_CAPTURE_TIME,
- configurationItemStatus, DbInstanceConfigEventBuilder.RESOURCE_ID,
- MessageType.ConfigurationItemChangeNotification.toString()));
- event.setRuleParameters(String.format(PARAMETERS_FORMAT, ACTUAL_BACKUP_WINDOW, ACTUAL_RENTENTION_PERIOD));
- invokeAndAssertRuleCompliance(ComplianceType.NOT_APPLICABLE, ResourceType.AWSEC2Instance);
- }
-
- @Test
- public void testException_scheduledNotificationMessage() throws IOException {
- event.setInvokingEvent(DbInstanceConfigEventBuilder.buildInvokingEvent(ACTUAL_BACKUP_WINDOW,
- ACTUAL_RENTENTION_PERIOD, configurationItemStatus, MessageType.ScheduledNotification));
- event.setRuleParameters(String.format(PARAMETERS_FORMAT, ACTUAL_BACKUP_WINDOW, ACTUAL_RENTENTION_PERIOD));
- invokeAndAssertException(String.format(DbInstanceBackupParameters.INCOMPATIBLE_EVENT_TYPES_MESSAGE,
- MessageType.ScheduledNotification.toString()));
- }
-
- private void invokeAndAssertException(String expectedMessage) throws IOException {
- try {
- dbInstanceBackupParameters.doHandle(event, context, configClient);
- fail("expected FuntionExecution exception");
- } catch (FunctionExecutionException ex) {
- assertEquals(expectedMessage, ex.getMessage());
- }
- }
-
- private void invokeAndAssertRuleCompliance(ComplianceType complianceType, ResourceType resourceType)
- throws IOException {
- dbInstanceBackupParameters.doHandle(event, context, configClient);
- verifyCompliance(complianceType, resourceType);
- }
-
- private void verifyCompliance(ComplianceType complianceType, ResourceType resourceType) {
- Evaluation evaluation = new Evaluation()
- .withComplianceResourceId(DbInstanceConfigEventBuilder.RESOURCE_ID)
- .withComplianceResourceType(resourceType.toString())
- .withOrderingTimestamp(DbInstanceConfigEventBuilder.CI_CAPTURE_TIME_IN_DATE_FORMAT)
- .withComplianceType(complianceType);
- PutEvaluationsRequest putEvaluationsRequest = new PutEvaluationsRequest()
- .withEvaluations(evaluation)
- .withResultToken(RESULT_TOKEN);
- verify(configClient).putEvaluations(eq(putEvaluationsRequest));
- }
-
- public static class DbInstanceConfigEventBuilder {
- public static final String CI_CAPTURE_TIME = "2016-07-26T16:03:58.070Z";
- public static final String RESOURCE_ID = "bar";
- public static final Date CI_CAPTURE_TIME_IN_DATE_FORMAT = Date.from(Instant
- .from(DateTimeFormatter.ISO_INSTANT.parse(CI_CAPTURE_TIME)));
-
- //@formatter:off
- private static final String DB_INSTANCE_CONFIG_EVENT =
- "{"
- + "\"configurationItem\":{"
- + "\"configuration\":{"
- + "\"preferredBackupWindow\":\"%s\","
- + "\"backupRetentionPeriod\":%s"
- + "},"
- + "\"configurationItemCaptureTime\":\"%s\","
- + "\"configurationItemStatus\":\"%s\","
- + "\"resourceType\":\"AWS::RDS::DBInstance\","
- + "\"resourceId\":\"%s\""
- + "},"
- + "\"messageType\":\"%s\""
- + "}";
- //@formatter:on
-
- public static String buildInvokingEvent(String preferredBackupWindow, String backupRentionPeriod,
- String configurationItemStatus, MessageType messageType) {
- return String.format(DB_INSTANCE_CONFIG_EVENT, preferredBackupWindow, backupRentionPeriod, CI_CAPTURE_TIME,
- configurationItemStatus, RESOURCE_ID, messageType.toString());
- }
- }
-}
-
diff --git a/vendor/github.com/awslabs/aws-config-rules/java/src/test/java/com/amazonaws/services/config/samplerules/DesiredInstanceTenancyTest.java b/vendor/github.com/awslabs/aws-config-rules/java/src/test/java/com/amazonaws/services/config/samplerules/DesiredInstanceTenancyTest.java
deleted file mode 100644
index eaa051a..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/java/src/test/java/com/amazonaws/services/config/samplerules/DesiredInstanceTenancyTest.java
+++ /dev/null
@@ -1,281 +0,0 @@
-package com.amazonaws.services.config.samplerules;
-
-import static org.mockito.Matchers.*;
-import static org.mockito.Mockito.*;
-
-import java.io.IOException;
-import java.time.Instant;
-import java.time.format.DateTimeFormatter;
-import java.util.Collections;
-import java.util.Date;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.runners.MockitoJUnitRunner;
-
-import com.amazonaws.services.config.AmazonConfig;
-import com.amazonaws.services.config.model.*;
-import com.amazonaws.services.config.samplerules.exception.FunctionExecutionException;
-import com.amazonaws.services.lambda.runtime.Context;
-import com.amazonaws.services.lambda.runtime.events.ConfigEvent;
-
-@RunWith(MockitoJUnitRunner.class)
-public class DesiredInstanceTenancyTest {
-
- private static final String RESULT_TOKEN = "resultToken";
- private static final String ACCOUNT_ID = "accountId";
-
- private static final String CI_CAPTURED_TIME = "2016-05-04T18:13:21.605Z";
- private static final String CONFIGURATION_ITEM_FORMAT = "\"resourceType\":\"%s\","
- + "\"resourceId\":\"%s\",\"configurationItemStatus\":\"%s\",\"configurationItemCaptureTime\": \"%s\",\"configuration\":%s";
- private static final String CONFIGURATION_FORMAT = " {\"imageId\":%s, \"placement\": {\"hostId\":%s, \"tenancy\": %s}}";
- private static final String INVOKING_EVENT_FORMAT = "{\"messageType\":%s,\"configurationItem\":{%s}}";
- private static final String PARAMETERS_FORMAT = "{\"tenancy\": %s,\"imageId\":%s,\"hostId\":%s}";
- private static final String CONFIGURATION_FORMAT_WITHOUT_HOST = "{\"imageId\":%s, \"placement\": {\"tenancy\": %s}}";
- private static final String SNAPSHOT_DELIVERY_INVOKING_EVENT_FORMAT = "{\"messageType\":%s}";
-
- private static final String RESOURCE_ID = "resourceId";
- private static final String DESIRED_HOST_ID = "dummyHostId";
- private static final String DESIRED_IMAGE_ID = "dummyImageId";
- private static final String OTHER_HOST_ID = "OtherHostId";
- private static final String OTHER_IMAGE_ID = "OtherImageId";
- private static final String DEDICATED_TENANCY = "dedicated";
- private static final String HOST_TENANCY = "host";
- private static final String DEFAULT_TENANCY = "default";
- private static final String STATUS_OK = "OK";
- private static final String RESOURCE_DELETED = "ResourceDeleted";
- private static final String RESOURCE_NOT_RECORDED = "ResourceNotRecorded";
-
- @Mock
- private Context context;
- @Mock
- private AmazonConfig configClient;
-
- private ConfigEvent event;
- private DesiredInstanceTenancy desiredInstanceTenancyFunction;
-
- @Before
- public void setUp() throws Exception {
- event = new ConfigEvent();
- event.setAccountId(ACCOUNT_ID);
- event.setResultToken(RESULT_TOKEN);
- event.setEventLeftScope(false);
- desiredInstanceTenancyFunction = new DesiredInstanceTenancy();
- when(configClient.putEvaluations(any(PutEvaluationsRequest.class)))
- .thenReturn(new PutEvaluationsResult().withFailedEvaluations(Collections.emptyList()));
- }
-
- @Test
- public void test_Compliant_withAllParameters() throws Exception {
- buildAndSetInvokingEvent(DEDICATED_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- setRuleParameters(DEDICATED_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- invokeFunctionAndAssertCompliance(ComplianceType.COMPLIANT);
- }
-
- @Test
- public void test_Compliant_withTenancyAndImageId() throws Exception {
- buildAndSetInvokingEvent(DEDICATED_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- setRuleParameters(DEDICATED_TENANCY, DESIRED_IMAGE_ID, null);
- invokeFunctionAndAssertCompliance(ComplianceType.COMPLIANT);
- }
-
- @Test
- public void test_Compliant_withHostTenancy() throws Exception {
- buildAndSetInvokingEvent(HOST_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- setRuleParameters(HOST_TENANCY, null, null);
- invokeFunctionAndAssertCompliance(ComplianceType.COMPLIANT);
- }
-
- @Test
- public void test_Compliant_withNoHostIdInCI() throws Exception {
- buildAndSetInvokingEvent(DEDICATED_TENANCY, DESIRED_IMAGE_ID, null);
- setRuleParameters(DEDICATED_TENANCY, DESIRED_IMAGE_ID, null);
- invokeFunctionAndAssertCompliance(ComplianceType.COMPLIANT);
- }
-
- @Test
- public void test_Compliant_withTenancyAndHostId() throws Exception {
- buildAndSetInvokingEvent(DEDICATED_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- setRuleParameters(DEDICATED_TENANCY, null, DESIRED_HOST_ID);
- invokeFunctionAndAssertCompliance(ComplianceType.COMPLIANT);
- }
-
- @Test
- public void test_Compliant_withUppercaseTenancy() throws IOException {
- buildAndSetInvokingEvent(DEDICATED_TENANCY, DESIRED_IMAGE_ID, null);
- setRuleParameters("DEDICATED", DESIRED_IMAGE_ID, null);
- invokeFunctionAndAssertCompliance(ComplianceType.COMPLIANT);
- }
-
- @Test
- public void test_Compliant_withDedicatedTenancy() throws Exception {
- buildAndSetInvokingEvent(DEDICATED_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- setRuleParameters(DEDICATED_TENANCY, null, null);
- invokeFunctionAndAssertCompliance(ComplianceType.COMPLIANT);
- }
-
- @Test
- public void test_NonCompliant_withDesiredTenancy_undesiredHostId() throws Exception {
- buildAndSetInvokingEvent(DEDICATED_TENANCY, DESIRED_IMAGE_ID, OTHER_HOST_ID);
- setRuleParameters(DEDICATED_TENANCY, null, DESIRED_HOST_ID);
- invokeFunctionAndAssertCompliance(ComplianceType.NON_COMPLIANT);
- }
-
- @Test
- public void test_NonCompliant_withUnDesiredTenancy_desiredImageHostId() throws Exception {
- buildAndSetInvokingEvent(HOST_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- setRuleParameters(DEDICATED_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- invokeFunctionAndAssertCompliance(ComplianceType.NON_COMPLIANT);
- }
-
- @Test
- public void test_NonCompliant_withUnDesiredHostTenancy() throws Exception {
- buildAndSetInvokingEvent(HOST_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- setRuleParameters(DEDICATED_TENANCY, null, null);
- invokeFunctionAndAssertCompliance(ComplianceType.NON_COMPLIANT);
- }
-
- @Test
- public void test_NonCompliant_withNoHostIdInCI() throws Exception {
- buildAndSetInvokingEventWithoutHostId(DEFAULT_TENANCY, DESIRED_IMAGE_ID);
- setRuleParameters(DEDICATED_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- invokeFunctionAndAssertCompliance(ComplianceType.NON_COMPLIANT);
- }
-
- @Test
- public void test_NonCompliant_withUnDesiredTenancy() throws Exception {
- buildAndSetInvokingEvent(DEFAULT_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- setRuleParameters(DEDICATED_TENANCY, null, null);
- invokeFunctionAndAssertCompliance(ComplianceType.NON_COMPLIANT);
- }
-
- @Test
- public void test_NotApplicable_eventLeftScope() throws Exception {
- buildAndSetInvokingEvent(DEDICATED_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- setRuleParameters(DEDICATED_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- event.setEventLeftScope(true);
- invokeFunctionAndAssertCompliance(ComplianceType.NOT_APPLICABLE);
- }
-
- @Test
- public void test_NotApplicable_withAllUndesiredParameters() throws Exception {
- buildAndSetInvokingEvent(HOST_TENANCY, OTHER_IMAGE_ID, OTHER_HOST_ID);
- setRuleParameters(DEDICATED_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- invokeFunctionAndAssertCompliance(ComplianceType.NOT_APPLICABLE);
- }
-
- @Test
- public void test_NotApplicable_withDesiredTenancy_undesiredImageHostId() throws Exception {
- buildAndSetInvokingEvent(DEDICATED_TENANCY, OTHER_IMAGE_ID, OTHER_HOST_ID);
- setRuleParameters(DEDICATED_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- invokeFunctionAndAssertCompliance(ComplianceType.NOT_APPLICABLE);
- }
-
- @Test
- public void test_NotApplicable_withDesiredTenancy_undesiredImageId() throws Exception {
- buildAndSetInvokingEvent(DEDICATED_TENANCY, OTHER_IMAGE_ID, DESIRED_HOST_ID);
- setRuleParameters(DEDICATED_TENANCY, DESIRED_IMAGE_ID, null);
- invokeFunctionAndAssertCompliance(ComplianceType.NOT_APPLICABLE);
- }
-
- @Test
- public void test_NotApplicableCase_DeletedResources() throws IOException {
- String configuration = String.format(CONFIGURATION_FORMAT, escapeStringToJson(DESIRED_IMAGE_ID),
- escapeStringToJson(DESIRED_HOST_ID), escapeStringToJson(DEDICATED_TENANCY));
- event.setInvokingEvent(buildConfigurationItem(configuration, RESOURCE_DELETED));
- setRuleParameters(DEDICATED_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- invokeFunctionAndAssertCompliance(ComplianceType.NOT_APPLICABLE);
- }
-
- @Test
- public void test_NotApplicableCase_notRecordedResources() throws IOException {
- String configuration = String.format(CONFIGURATION_FORMAT, escapeStringToJson(DESIRED_IMAGE_ID),
- escapeStringToJson(DESIRED_HOST_ID), escapeStringToJson(DEDICATED_TENANCY));
- event.setInvokingEvent(buildConfigurationItem(configuration, RESOURCE_NOT_RECORDED));
- setRuleParameters(DEDICATED_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- invokeFunctionAndAssertCompliance(ComplianceType.NOT_APPLICABLE);
- }
-
- @Test(expected = FunctionExecutionException.class)
- public void testHandleWrongMessageType() throws IOException {
- event.setInvokingEvent(String.format(SNAPSHOT_DELIVERY_INVOKING_EVENT_FORMAT,
- escapeStringToJson(MessageType.ConfigurationSnapshotDeliveryCompleted.toString())));
- desiredInstanceTenancyFunction.doHandle(event, context, configClient);
- }
-
- @Test(expected = FunctionExecutionException.class)
- public void testHandleFailedEvaluations() throws IOException {
- buildAndSetInvokingEvent(DEDICATED_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- setRuleParameters(DEDICATED_TENANCY, DESIRED_IMAGE_ID, DESIRED_HOST_ID);
- Evaluation failedEvaluation = new Evaluation();
- when(configClient.putEvaluations(any(PutEvaluationsRequest.class)))
- .thenReturn(new PutEvaluationsResult().withFailedEvaluations(failedEvaluation));
- desiredInstanceTenancyFunction.doHandle(event, context, configClient);
- }
-
- private void buildAndSetInvokingEvent(String tenancy, String imageId, String hostId) {
- event.setInvokingEvent(buildInstanceWithTenancyHostImageIds(tenancy, imageId, hostId,
- STATUS_OK));
- }
-
- private void buildAndSetInvokingEventWithoutHostId(String tenancy, String imageId) {
- event.setInvokingEvent(buildInstanceWithoutHostId(tenancy, imageId, STATUS_OK));
- }
-
- private void setRuleParameters(String expectedTenancy, String expectedImageId, String expectedHostId) {
- event.setRuleParameters(String.format(PARAMETERS_FORMAT, escapeStringToJson(expectedTenancy),
- escapeStringToJson(expectedImageId),
- escapeStringToJson(expectedHostId)));
- }
-
- private void invokeFunctionAndAssertCompliance(ComplianceType expectedCompliance) throws IOException {
- desiredInstanceTenancyFunction.doHandle(event, context, configClient);
- verifyReportedCompliance(expectedCompliance);
- }
-
- private String buildInstanceWithTenancyHostImageIds(String tenancy, String imageId, String hostId,
- String configurationItemStatus) {
- String configuration = String.format(CONFIGURATION_FORMAT, escapeStringToJson(imageId),
- escapeStringToJson(hostId), escapeStringToJson(tenancy));
- return buildConfigurationItem(configuration, configurationItemStatus);
- }
-
- private String buildInstanceWithoutHostId(String tenancy, String imageId, String configurationItemStatus) {
- String configuration = String.format(CONFIGURATION_FORMAT_WITHOUT_HOST, escapeStringToJson(imageId),
- escapeStringToJson(tenancy));
- return buildConfigurationItem(configuration, configurationItemStatus);
- }
-
- private String buildConfigurationItem(String configuration, String configurationItemStatus) {
- String configurationItem = String.format(CONFIGURATION_ITEM_FORMAT, ResourceType.AWSEC2Instance.toString(),
- RESOURCE_ID, configurationItemStatus, CI_CAPTURED_TIME, configuration);
- return String.format(INVOKING_EVENT_FORMAT,
- escapeStringToJson(MessageType.ConfigurationItemChangeNotification.toString()),
- configurationItem);
- }
-
- private void verifyReportedCompliance(ComplianceType compliance) {
- Evaluation evaluation = new Evaluation()
- .withComplianceResourceId(RESOURCE_ID)
- .withComplianceResourceType(ResourceType.AWSEC2Instance.toString())
- .withOrderingTimestamp(getDate(CI_CAPTURED_TIME))
- .withComplianceType(compliance);
- PutEvaluationsRequest putEvaluationsRequest = new PutEvaluationsRequest()
- .withEvaluations(evaluation)
- .withResultToken(RESULT_TOKEN);
- verify(configClient).putEvaluations(eq(putEvaluationsRequest));
- }
-
- private String escapeStringToJson(String string) {
- if (string != null) {
- string = String.format("\"%s\"", string);
- }
- return string;
- }
-
- private Date getDate(String dateString) {
- return Date.from(Instant.from(DateTimeFormatter.ISO_INSTANT.parse(dateString)));
- }
-}
diff --git a/vendor/github.com/awslabs/aws-config-rules/java/src/test/java/com/amazonaws/services/config/samplerules/RootAccountMFAEnabledTest.java b/vendor/github.com/awslabs/aws-config-rules/java/src/test/java/com/amazonaws/services/config/samplerules/RootAccountMFAEnabledTest.java
deleted file mode 100644
index bdc756a..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/java/src/test/java/com/amazonaws/services/config/samplerules/RootAccountMFAEnabledTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package com.amazonaws.services.config.samplerules;
-
-import static org.mockito.Matchers.*;
-import static org.mockito.Mockito.*;
-
-import java.io.IOException;
-import java.util.Collections;
-import java.util.Date;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.runners.MockitoJUnitRunner;
-
-import com.amazonaws.services.config.AmazonConfig;
-import com.amazonaws.services.config.model.*;
-import com.amazonaws.services.config.samplerules.exception.FunctionExecutionException;
-import com.amazonaws.services.identitymanagement.AmazonIdentityManagement;
-import com.amazonaws.services.identitymanagement.model.GetAccountSummaryResult;
-import com.amazonaws.services.lambda.runtime.Context;
-import com.amazonaws.services.lambda.runtime.events.ConfigEvent;
-
-@RunWith(MockitoJUnitRunner.class)
-public class RootAccountMFAEnabledTest {
-
- private static final String RESULT_TOKEN = "resultToken";
- private static final String ACCOUNT_ID = "accountId";
- private static final String ACCOUNT_MFA_ENABLED = "AccountMFAEnabled";
- private static final String ACCOUNT_TYPE = "AWS::::Account";
- /**
- * This test event contains a subset of the parameters for AWS Config events. For all parameters see:
- * http://docs.aws.amazon.com/config/latest/developerguide/evaluate-config_develop-rules_example-events.html
- */
- private static final String INVOKING_EVENT_FORMAT = "{\"messageType\":\"%s\"}";
- private static final Date NOW = new Date();
-
- @Mock
- private Context context;
- @Mock
- private AmazonConfig configClient;
- @Mock
- private AmazonIdentityManagement iamClient;
- private ConfigEvent event;
- private GetAccountSummaryResult accountSummary;
- private RootAccountMFAEnabled mfaEnabledFunction;
-
- @Before
- public void setup() {
- event = new ConfigEvent();
- event.setInvokingEvent(String.format(INVOKING_EVENT_FORMAT, MessageType.ScheduledNotification));
- event.setAccountId(ACCOUNT_ID);
- event.setResultToken(RESULT_TOKEN);
- accountSummary = new GetAccountSummaryResult();
- mfaEnabledFunction = new RootAccountMFAEnabled();
- when(configClient.putEvaluations(any(PutEvaluationsRequest.class)))
- .thenReturn(new PutEvaluationsResult().withFailedEvaluations(Collections.emptyList()));
- when(iamClient.getAccountSummary()).thenReturn(accountSummary);
- }
-
- @Test
- public void testHandleCompliant() throws IOException {
- accountSummary.getSummaryMap().put(ACCOUNT_MFA_ENABLED, 1);
- mfaEnabledFunction.doHandle(event, context, configClient, iamClient, () -> NOW);
- verify(configClient).putEvaluations(buildPutEvaluationsRequest(ComplianceType.COMPLIANT));
- }
-
- @Test
- public void testHandleNonCompliant() throws IOException {
- accountSummary.getSummaryMap().put(ACCOUNT_MFA_ENABLED, 0);
- mfaEnabledFunction.doHandle(event, context, configClient, iamClient, () -> NOW);
- verify(configClient).putEvaluations(buildPutEvaluationsRequest(ComplianceType.NON_COMPLIANT));
- }
-
- @Test
- public void testHandleNonCompliantNullCount() throws IOException {
- accountSummary.getSummaryMap().put(ACCOUNT_MFA_ENABLED, null);
- mfaEnabledFunction.doHandle(event, context, configClient, iamClient, () -> NOW);
- verify(configClient).putEvaluations(buildPutEvaluationsRequest(ComplianceType.NON_COMPLIANT));
- }
-
- @Test
- public void testHandleNonCompliantPropertyMissing() throws IOException {
- mfaEnabledFunction.doHandle(event, context, configClient, iamClient, () -> NOW);
- verify(configClient).putEvaluations(buildPutEvaluationsRequest(ComplianceType.NON_COMPLIANT));
- }
-
- @Test(expected = FunctionExecutionException.class)
- public void testHandleWrongMessageType() throws IOException {
- accountSummary.getSummaryMap().put(ACCOUNT_MFA_ENABLED, 0);
- event.setInvokingEvent(String.format(INVOKING_EVENT_FORMAT, MessageType.ConfigurationItemChangeNotification));
- mfaEnabledFunction.doHandle(event, context, configClient, iamClient, () -> NOW);
- }
-
- @Test(expected = FunctionExecutionException.class)
- public void testHandleFailedEvaluations() throws IOException {
- accountSummary.getSummaryMap().put(ACCOUNT_MFA_ENABLED, 0);
- Evaluation failedEvaluation = new Evaluation();
- when(configClient.putEvaluations(any(PutEvaluationsRequest.class)))
- .thenReturn(new PutEvaluationsResult().withFailedEvaluations(failedEvaluation));
- mfaEnabledFunction.doHandle(event, context, configClient, iamClient, () -> NOW);
- }
-
- private PutEvaluationsRequest buildPutEvaluationsRequest(ComplianceType compliance) {
- Evaluation evaluation = new Evaluation()
- .withComplianceResourceId(ACCOUNT_ID)
- .withComplianceResourceType(ACCOUNT_TYPE)
- .withOrderingTimestamp(NOW)
- .withComplianceType(compliance);
- return new PutEvaluationsRequest()
- .withEvaluations(evaluation)
- .withResultToken(RESULT_TOKEN);
- }
-
-}
diff --git a/vendor/github.com/awslabs/aws-config-rules/node/iam_access_key_rotation-triggered.js b/vendor/github.com/awslabs/aws-config-rules/node/iam_access_key_rotation-triggered.js
deleted file mode 100755
index de8abe6..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/node/iam_access_key_rotation-triggered.js
+++ /dev/null
@@ -1,119 +0,0 @@
-//
-// This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-//
-// Ensure IAM User Access Key Rotation
-// Description: Checks that the IAM User's Access Keys have been rotated within the specified number of days.
-//
-// Trigger Type: Change Triggered
-// Scope of Changes: IAM:User
-// Required Parameter: MaximumAccessKeyAge
-// Example Value: 90
-
-var aws = require('aws-sdk');
-var config = new aws.ConfigService();
-var iam = new aws.IAM();
-
-// Helper function used to validate input
-function checkDefined(reference, referenceName) {
-
- if (!reference) {
-
- console.log("Error: " + referenceName + " is not defined");
- throw referenceName;
-
- }
-
- return reference;
-
-}
-
-// Check whether the the resource has been deleted. If it has, then the evaluation is unnecessary.
-function isApplicable(configurationItem, event) {
-
- checkDefined(configurationItem, "configurationItem");
- checkDefined(event, "event");
-
- var status = configurationItem.configurationItemStatus;
- var eventLeftScope = event.eventLeftScope;
-
- return ('OK' === status || 'ResourceDiscovered' === status) && false === eventLeftScope;
-
-}
-
-// This is the handler that's invoked by Lambda
-exports.handler = function(event, context) {
-
- event = checkDefined(event, "event");
- var invokingEvent = JSON.parse(event.invokingEvent);
- var ruleParameters = JSON.parse(event.ruleParameters);
- var configurationItem = checkDefined(invokingEvent.configurationItem, "invokingEvent.configurationItem");
- var compliance = 'NOT_APPLICABLE';
- var putEvaluationsRequest = {};
-
- // Only run check on IAM Users
- if (configurationItem.resourceType === 'AWS::IAM::User') {
-
- // List all Access Keys for user
- iam.listAccessKeys({ UserName: configurationItem.resourceName }, function(keyerr, keydata) {
-
- var ret = 'NOT_APPLICABLE';
-
- if (!keyerr) {
-
- // Only check dates on users with keys
- if (keydata.AccessKeyMetadata.length > 0) {
-
- // Check all keys
- for (var k = 0; k < keydata.AccessKeyMetadata.length; k++) {
-
- var now = Date.now();
-
- if (Math.floor((now - Date.parse(keydata.AccessKeyMetadata[k].CreateDate)) / 86400000) > ruleParameters.MaximumAccessKeyAge) {
-
- ret = 'NON_COMPLIANT';
-
- } else {
-
- ret = 'COMPLIANT';
-
- }
-
- }
- }
-
- } else {
-
- console.log(keyerr);
-
- }
-
- putEvaluationsRequest.Evaluations = [{
- ComplianceResourceType: configurationItem.resourceType,
- ComplianceResourceId: configurationItem.resourceId,
- ComplianceType: ret,
- OrderingTimestamp: configurationItem.configurationItemCaptureTime
- }];
-
- putEvaluationsRequest.ResultToken = event.resultToken;
-
- // Invoke the Config API to report the result of the evaluation
- config.putEvaluations(putEvaluationsRequest, function (err, data) {
- if (err) {
- context.fail(err);
- } else {
- context.succeed(data);
- }
- });
-
- });
-
- } else {
-
- // NOT APPLICABLE
- putEvaluationsRequest.Evaluations = [ { ComplianceResourceType: configurationItem.resourceType, ComplianceResourceId: configurationItem.resourceId, ComplianceType: compliance, OrderingTimestamp: configurationItem.configurationItemCaptureTime } ];
- putEvaluationsRequest.ResultToken = event.resultToken;
- config.putEvaluations(putEvaluationsRequest, function (err, data) { if (err) { context.fail(err); } else { context.succeed(data); } });
-
- }
-
-};
diff --git a/vendor/github.com/awslabs/aws-config-rules/node/iam_mfa_require-triggered.js b/vendor/github.com/awslabs/aws-config-rules/node/iam_mfa_require-triggered.js
deleted file mode 100755
index 37a683b..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/node/iam_mfa_require-triggered.js
+++ /dev/null
@@ -1,97 +0,0 @@
-//
-// This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-//
-// Ensure IAM User has MFA Enabled
-// Description: Checks that all IAM Users have MFA Enabled
-//
-// Trigger Type: Change Triggered
-// Scope of Changes: IAM:User
-// Required Parameter: None
-
-var aws = require('aws-sdk');
-var config = new aws.ConfigService();
-var iam = new aws.IAM();
-
-// Helper function used to validate input
-function checkDefined(reference, referenceName) {
- if (!reference) {
- console.log("Error: " + referenceName + " is not defined");
- throw referenceName;
- }
- return reference;
-}
-
-// Check whether the the resource has been deleted. If it has, then the evaluation is unnecessary.
-
-function isApplicable(configurationItem, event){
- checkDefined(configurationItem, "configurationItem");
- checkDefined(event, "event");
- var status = configurationItem.configurationItemStatus;
- var eventLeftScope = event.eventLeftScope;
- return ('OK' === status || 'ResourceDiscovered' === status) && false === eventLeftScope;
-}
-
-// This is the handler that's invoked by Lambda
-
-exports.handler = function(event, context) {
- event = checkDefined(event, "event");
- var invokingEvent = JSON.parse(event.invokingEvent);
- var ruleParameters = JSON.parse(event.ruleParameters);
- var configurationItem = checkDefined(invokingEvent.configurationItem, "invokingEvent.configurationItem");
- var putEvaluationsRequest = {};
-
- // Only call out Async if a User
- if (configurationItem.resourceType === 'AWS::IAM::User') {
-
- iam.listMFADevices({ UserName: configurationItem.resourceName }, function(mfaerr, mfadata) {
-
- var ret = 'NON_COMPLIANT';
-
- if (!mfaerr) {
-
- if (mfadata.MFADevices.length > 0) {
-
- ret = 'COMPLIANT';
-
- }
-
- } else {
-
- console.log(mfaerr);
-
- }
-
- putEvaluationsRequest.Evaluations = [{
- ComplianceResourceType: configurationItem.resourceType,
- ComplianceResourceId: configurationItem.resourceId,
- ComplianceType: ret,
- OrderingTimestamp: configurationItem.configurationItemCaptureTime
- }];
-
- putEvaluationsRequest.ResultToken = event.resultToken;
-
- // Invoke the Config API to report the result of the evaluation
- config.putEvaluations(putEvaluationsRequest, function (err, data) {
- if (err) {
- context.fail(err);
- } else {
- context.succeed(data);
- }
- });
-
- });
-
- } else {
-
- // Put together the request that reports the evaluation status
- // Note that we're choosing to report this evaluation against the resource that was passed in.
- // You can choose to report this against any other resource type, as long as it is supported by Config rules
- putEvaluationsRequest.Evaluations = [ { ComplianceResourceType: configurationItem.resourceType, ComplianceResourceId: configurationItem.resourceId, ComplianceType: 'NOT_APPLICABLE', OrderingTimestamp: configurationItem.configurationItemCaptureTime } ];
- putEvaluationsRequest.ResultToken = event.resultToken;
-
- // Invoke the Config API to report the result of the evaluation
- config.putEvaluations(putEvaluationsRequest, function (err, data) { if (err) { context.fail(err); } else { context.succeed(data); } });
-
- }
-
-};
diff --git a/vendor/github.com/awslabs/aws-config-rules/node/instance_desired_tenancy-triggered.js b/vendor/github.com/awslabs/aws-config-rules/node/instance_desired_tenancy-triggered.js
deleted file mode 100755
index b6d2330..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/node/instance_desired_tenancy-triggered.js
+++ /dev/null
@@ -1,81 +0,0 @@
-//
-// This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-//
-// Ensure EC2 Instances have desired tenancy
-// Description: Checks that EC2 Instances have desired tenancy
-//
-// Trigger Type: Change Triggered
-// Scope of Changes: EC2:Instance
-// Required Parameter: DesiredTenancy
-// Example Value: dedicated
-
-var aws = require('aws-sdk');
-var config = new aws.ConfigService();
-// This is where it's determined whether the resource is compliant or not.
-// In this example, we look at the tenancy of the EC2 instance and determine whether it matches
-// the "DesiredTenancy" parameter that is passed to the rule. If the tenancy is not of the DesiredTenancy type, the
-// instance is marked non-compliant. Otherwise, it is marked complaint.
-
-function evaluateCompliance(configurationItem, ruleParameters, context) {
- checkDefined(configurationItem, "configurationItem");
- checkDefined(configurationItem.configuration, "configurationItem.configuration");
- checkDefined(ruleParameters, "ruleParameters");
- if ('AWS::EC2::Instance' !== configurationItem.resourceType) {
- return 'NOT_APPLICABLE';
- } if (ruleParameters.DesiredTenancy === configurationItem.configuration.placement.tenancy) {
- return 'COMPLIANT';
- } else {
- return 'NON_COMPLIANT';
- }
-}
-// Helper function used to validate input
-function checkDefined(reference, referenceName) {
- if (!reference) {
- console.log("Error: " + referenceName + " is not defined");
- throw referenceName;
- }
- return reference;
-}
-// Check whether the the resource has been deleted. If it has, then the evaluation is unnecessary.
-function isApplicable(configurationItem, event) {
- checkDefined(configurationItem, "configurationItem");
- checkDefined(event, "event");
- var status = configurationItem.configurationItemStatus;
- var eventLeftScope = event.eventLeftScope;
- return ('OK' === status || 'ResourceDiscovered' === status) && false === eventLeftScope;
-}
-// This is the handler that's invoked by Lambda
-// Most of this code is boilerplate; use as is
-exports.handler = function(event, context) {
- event = checkDefined(event, "event");
- var invokingEvent = JSON.parse(event.invokingEvent);
- var ruleParameters = JSON.parse(event.ruleParameters);
- var configurationItem = checkDefined(invokingEvent.configurationItem, "invokingEvent.configurationItem");
- var compliance = 'NOT_APPLICABLE';
- var putEvaluationsRequest = {};
- if (isApplicable(invokingEvent.configurationItem, event)) {
- // Invoke the compliance checking function.
- compliance = evaluateCompliance(invokingEvent.configurationItem, ruleParameters, context);
- }
- // Put together the request that reports the evaluation status
- // Note that we're choosing to report this evaluation against the resource that was passed in.
- // You can choose to report this against any other resource type, as long as it is supported by Config rules
-
- putEvaluationsRequest.Evaluations = [
- {
- ComplianceResourceType: configurationItem.resourceType,
- ComplianceResourceId: configurationItem.resourceId,
- ComplianceType: compliance,
- OrderingTimestamp: configurationItem.configurationItemCaptureTime
- }
- ];
- putEvaluationsRequest.ResultToken = event.resultToken;
- // Invoke the Config API to report the result of the evaluation
- config.putEvaluations(putEvaluationsRequest, function (err, data) {
- if (err) {
- context.fail(err);
- } else {
- context.succeed(data);
- }
- });
-};
diff --git a/vendor/github.com/awslabs/aws-config-rules/node/rds_db_instance_encrypted.js b/vendor/github.com/awslabs/aws-config-rules/node/rds_db_instance_encrypted.js
deleted file mode 100644
index 2453aa3..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/node/rds_db_instance_encrypted.js
+++ /dev/null
@@ -1,101 +0,0 @@
-//
-// This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-//
-// RDS DB Instances are encrypted and if an optional KMS Key ARN parameter is provided, we check whether the DB Instances were encrypted using the specified key
-//
-// Trigger Type: Change Triggered
-// Scope of Changes: RDS:Instance
-// Accepted Parameters: KMSKeyARN (optional)
-// Example Values: 'arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab'
-
-'use strict';
-let aws = require('aws-sdk');
-let config = new aws.ConfigService();
-
-// This is where it's determined whether the resource is compliant or not.
-function evaluateCompliance(configurationItem, ruleParameters) {
- checkDefined(configurationItem, 'configurationItem');
- checkDefined(configurationItem.configuration, 'configurationItem.configuration');
- checkDefined(ruleParameters, 'ruleParameters');
- // If the resource is not a RDS DBInstance, then we deem this resource to be not applicable. (If the scope of the rule is specified to include only
- // RDS DB Instances, this rule is invoked only for DB Instances.)
- if ('AWS::RDS::DBInstance' !== configurationItem.resourceType) {
- return 'NOT_APPLICABLE';
- }
-
- if (configurationItem.configuration.storageEncrypted)
- {
- //If KMS Key is provided as a rule parameter, check if the dbinstance is using this key for encryption
- if(ruleParameters.KMSKeyARN)
- {
- // Encrypted with correct key
- if(ruleParameters.KMSKeyARN === configurationItem.configuration.kmsKeyId){
- return 'COMPLIANT';
- }
- // Encrypted but not with specified KMS key
- return 'NON_COMPLIANT';
- }
- // Encrypted, no specific key needed
- return 'COMPLIANT';
- }
- else {
- // Not encrypted
- return 'NON_COMPLIANT';
- }
-}
-
-// Helper function used to validate input
-
-function checkDefined(reference, referenceName) {
- if (!reference) {
- console.log('Error: ${referenceName} is not defined');
- throw referenceName;
- }
- return reference;
-}
-
-// Check whether the the resource has been deleted. If it has, then the evaluation is unnecessary.
-
-function isApplicable(configurationItem, event) {
- checkDefined(configurationItem, 'configurationItem');
- checkDefined(event, 'event');
- const status = configurationItem.configurationItemStatus;
- const eventLeftScope = event.eventLeftScope;
- return ('OK' === status || 'ResourceDiscovered' === status) && false === eventLeftScope;
-}
-
-// This is the handler that's invoked by Lambda
-// Most of this code is boilerplate; use as is
-
-exports.handler = (event, context, callback) => {
- event = checkDefined(event, 'event');
- console.log('Received event:' + JSON.stringify(event, null, 4));
- const invokingEvent = JSON.parse(event.invokingEvent);
- const ruleParameters = "ruleParameters" in event ? JSON.parse(event.ruleParameters) : {};
- const configurationItem = checkDefined(invokingEvent.configurationItem, 'invokingEvent.configurationItem');
- let compliance = 'NOT_APPLICABLE';
-
- if (isApplicable(invokingEvent.configurationItem, event)) {
- // Invoke the compliance checking function.
- compliance = evaluateCompliance(invokingEvent.configurationItem, ruleParameters);
- }
-
- // Put together the request that reports the evaluation status
- // Note that we're choosing to report this evaluation against the resource that was passed in.
- // You can choose to report this against any other resource type
-
- const evaluation = {
- ComplianceResourceType: configurationItem.resourceType,
- ComplianceResourceId: configurationItem.resourceId,
- ComplianceType: compliance,
- OrderingTimestamp: configurationItem.configurationItemCaptureTime
- };
- const putEvaluationsRequest = {
- Evaluations : [ evaluation ],
- ResultToken : event.resultToken
- };
-
- // Invoke the Config API to report the result of the evaluation
- console.log('Put Evaluation:' + JSON.stringify(evaluation, null, 4));
- config.putEvaluations(putEvaluationsRequest, callback);
-};
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ACM_CERTIFICATE_EXPIRATION_CHECK/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/ACM_CERTIFICATE_EXPIRATION_CHECK/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ACM_CERTIFICATE_EXPIRATION_CHECK/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ACM_CERTIFICATE_EXPIRATION_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/ACM_CERTIFICATE_EXPIRATION_CHECK/parameters.json
deleted file mode 100644
index cb36dbe..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ACM_CERTIFICATE_EXPIRATION_CHECK/parameters.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "ACM_CERTIFICATE_EXPIRATION_CHECK",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{\"daysToExpiration\": \"14\"}",
- "SourceEvents": "AWS::ACM::Certificate",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "ACM_CERTIFICATE_EXPIRATION_CHECK"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ALB_HTTP_TO_HTTPS_REDIRECTION_CHECK/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/ALB_HTTP_TO_HTTPS_REDIRECTION_CHECK/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ALB_HTTP_TO_HTTPS_REDIRECTION_CHECK/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ALB_HTTP_TO_HTTPS_REDIRECTION_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/ALB_HTTP_TO_HTTPS_REDIRECTION_CHECK/parameters.json
deleted file mode 100644
index 26f13d4..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ALB_HTTP_TO_HTTPS_REDIRECTION_CHECK/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "ALB_HTTP_TO_HTTPS_REDIRECTION_CHECK",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "ALB_HTTP_TO_HTTPS_REDIRECTION_CHECK"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/AMI_NOT_PUBLIC_CHECK/AMI_NOT_PUBLIC_CHECK.py b/vendor/github.com/awslabs/aws-config-rules/python/AMI_NOT_PUBLIC_CHECK/AMI_NOT_PUBLIC_CHECK.py
deleted file mode 100644
index 82308b1..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/AMI_NOT_PUBLIC_CHECK/AMI_NOT_PUBLIC_CHECK.py
+++ /dev/null
@@ -1,370 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-'''
-#####################################
-## Gherkin ##
-#####################################
-Rule Name:
- AMI_NOT_PUBLIC_CHECK
-
-Description:
- Check whether the Amazon Machine Images are not publicly accessible. The rule is NON_COMPLIANT if one or more Amazon Machine Images are publicly accessible.
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::::Account
-
-Rule Parameters:
- None
-
-Scenarios:
- Scenario: 1
- Given: No AMIs with "is-public" parameter set to True
- Then: Return COMPLIANT
- Scenario: 2
- Given: One or more AMIs with is-public parameter set to True
- Then: Return NON_COMPLIANT with Annotation containing AMI IDs
-'''
-
-import json
-import sys
-import datetime
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-# Generates list of image_id's of public images
-def generate_image_id_list(images, event):
- image_ids = []
- for image in images:
- image_ids.append(image['ImageId'])
- return image_ids
-
-def build_annotation(annotation_string):
- if len(annotation_string) > 256:
- return annotation_string[:244] + " [truncated]"
- return annotation_string
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- ec2_client = get_client('ec2', event)
- public_ami_result = ec2_client.describe_images(
- Filters=[
- {
- 'Name': 'is-public',
- 'Values': ['true']
- }
- ],
- Owners=[event['accountId']]
- )
- # If public_ami_list is not empty, generate non-compliant response
- if public_ami_result['Images']:
- evaluations = []
- evaluations.append(
- build_evaluation(
- event['accountId'],
- 'NON_COMPLIANT',
- event,
- annotation='Public Amazon Machine Image Id: {}'.format(",".join([generate_image_id_list(public_ami_result['Images'], event)]))
- )
- )
- return evaluations
- return build_evaluation(event['accountId'], "COMPLIANT", event)
-
-def evaluate_parameters(rule_parameters):
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = build_annotation(annotation)
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = build_annotation(annotation)
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return status in ('OK', 'ResourceDiscovered') and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/AMI_NOT_PUBLIC_CHECK/AMI_NOT_PUBLIC_CHECK_test.py b/vendor/github.com/awslabs/aws-config-rules/python/AMI_NOT_PUBLIC_CHECK/AMI_NOT_PUBLIC_CHECK_test.py
deleted file mode 100644
index 5b5a8df..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/AMI_NOT_PUBLIC_CHECK/AMI_NOT_PUBLIC_CHECK_test.py
+++ /dev/null
@@ -1,268 +0,0 @@
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-EC2_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 'ec2':
- return EC2_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('AMI_NOT_PUBLIC_CHECK')
-
-# Checks for scenario wherein no non-compliant resources are present
-class CompliantResourcesTest(unittest.TestCase):
- def test_scenario_1_compliant_resources(self):
- describe_images_result = {
- 'Images': [],
- 'ResponseMetadata': {}
- }
- EC2_CLIENT_MOCK.describe_images = MagicMock(return_value=describe_images_result)
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- expected_response = [
- build_expected_response(
- compliance_type='COMPLIANT',
- compliance_resource_id='123456789012'
- )
- ]
- assert_successful_evaluation(self, response, expected_response, len(response))
-
-# Checks for scenario wherein non-compliant resources are present
-class NonCompliantResourcesTest(unittest.TestCase):
- def test_scenario_2_non_compliant_resources(self):
- describe_images_result = {
- 'Images': [
- {
- 'ImageId': 'ami-040574eaefd6dc6d4',
- 'Public': True,
- 'OwnerId': '123456789012'
- },
- {
- 'ImageId': 'ami-0a1402bb0642906aa',
- 'Public': True,
- 'OwnerId': '123456789012'
- }
- ],
- 'ResponseMetadata': {}
- }
- EC2_CLIENT_MOCK.describe_images = MagicMock(return_value=describe_images_result)
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- expected_response = [
- build_expected_response(
- compliance_type='NON_COMPLIANT',
- compliance_resource_id='123456789012',
- annotation='Public Amazon Machine Image Id: ami-040574eaefd6dc6d4,ami-0a1402bb0642906aa'
- )
- ]
- assert_successful_evaluation(self, response, expected_response, len(response))
-
- def test_scenario_3_non_compliant_resources(self):
- describe_images_result = {
- 'Images': [
- {
- 'ImageId': 'ami-0a1402bb0642906ab',
- 'Public': True,
- 'OwnerId': '123456789012'
- },
- {
- 'ImageId': 'ami-0a1402bb0642906ac',
- 'Public': True,
- 'OwnerId': '123456789012'
- },
- {
- 'ImageId': 'ami-0a1402bb0642906ad',
- 'Public': True,
- 'OwnerId': '123456789012'
- },
- {
- 'ImageId': 'ami-0a1402bb0642906ae',
- 'Public': True,
- 'OwnerId': '123456789012'
- },
- {
- 'ImageId': 'ami-0a1402bb0642906af',
- 'Public': True,
- 'OwnerId': '123456789012'
- },
- {
- 'ImageId': 'ami-0a1402bb0642906ag',
- 'Public': True,
- 'OwnerId': '123456789012'
- },
- {
- 'ImageId': 'ami-0a1402bb0642906ah',
- 'Public': True,
- 'OwnerId': '123456789012'
- },
- {
- 'ImageId': 'ami-0a1402bb0642906ai',
- 'Public': True,
- 'OwnerId': '123456789012'
- },
- {
- 'ImageId': 'ami-0a1402bb0642906aj',
- 'Public': True,
- 'OwnerId': '123456789012'
- },
- {
- 'ImageId': 'ami-0a1402bb0642906ak',
- 'Public': True,
- 'OwnerId': '123456789012'
- },
- {
- 'ImageId': 'ami-0a1402bb0642906al',
- 'Public': True,
- 'OwnerId': '123456789012'
- },
- {
- 'ImageId': 'ami-0a1402bb0642906am',
- 'Public': True,
- 'OwnerId': '123456789012'
- }
- ],
- 'ResponseMetadata': {}
- }
- EC2_CLIENT_MOCK.describe_images = MagicMock(return_value=describe_images_result)
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- expected_response = [
- build_expected_response(
- compliance_type='NON_COMPLIANT',
- compliance_resource_id='123456789012',
- annotation='Public Amazon Machine Image Id: ami-0a1402bb0642906ab,ami-0a1402bb0642906ac,ami-0a1402bb0642906ad,ami-0a1402bb0642906ae,ami-0a1402bb0642906af,ami-0a1402bb0642906ag,ami-0a1402bb0642906ah,ami-0a1402bb0642906ai,ami-0a1402bb0642906aj,ami-0a1402bb06 [truncated]'
- )
- ]
- assert_successful_evaluation(self, response, expected_response, len(response))
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/AMI_NOT_PUBLIC_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/AMI_NOT_PUBLIC_CHECK/parameters.json
deleted file mode 100644
index f5a9a07..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/AMI_NOT_PUBLIC_CHECK/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "AMI_NOT_PUBLIC_CHECK",
- "SourceRuntime": "python3.6",
- "CodeKey": "AMI_NOT_PUBLIC_CHECK.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourcePeriodic": "Six_Hours"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/AMI_OUTDATED_CHECK/AMI_OUTDATED_CHECK.py b/vendor/github.com/awslabs/aws-config-rules/python/AMI_OUTDATED_CHECK/AMI_OUTDATED_CHECK.py
deleted file mode 100755
index e035eeb..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/AMI_OUTDATED_CHECK/AMI_OUTDATED_CHECK.py
+++ /dev/null
@@ -1,663 +0,0 @@
-"""
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- AMI_OUTDATED_CHECK
-
-Description:
- Check all private AMIs are not older than X days.
-
-Trigger:
- Periodic
-
-Resource Type to report on:
- AWS::EC2::Instance
-
-Rule Parameters:
- | -------------------- | --------- | --------------------------------------------- | ------------------|
- | Parameter Name | Type | Description | Notes |
- | -------------------- | --------- | --------------------------------------------- | ------------------|
- | NumberOfDays | Optional | The number of days against which the ami age | must be equal to |
- | | | of the instances will be compared. | or less than |
- | | | Default value: 90 days | |
- | -------------------- | --------- | --------------------------------------------- | ------------------|
- | WhitelistedAmis | Optional | The list of AMI Ids provided by the user to | must be equal to |
- | | | be excluded from the check. | item in list. |
- | -------------------- | --------- | --------------------------------------------- | ----------------- |
- | WhitelistedInstances | Optional | The list of Instance ID's provided by the | must be equal to |
- | | | user to be excluded from the check. | item in list. |
- |--------------------- |-----------|-----------------------------------------------|-------------------|
-
-Feature:
- In order to: to use latest AMI
- As: a Security Officer
- I want: To ensure that all AMIs of the account is not older than X number of days.
-
-Scenarios:
- Scenario 1:
- Given: NumberOfDays, WhitelistedAmis or WhitelistedInstances parameters are not present
- Then: Return value error
-
- Scenario 2:
- Given: NumberOfDays parameter is configured
- And: NumberOfDays parameter is not an integer
- Then: Return value error
-
- Scenario 3:
- Given: NumberOfDays parameter is configured
- And: NumberOfDays parameter is less than 1
- Then: Return value error
-
- Scenario 4:
- Given: NumberOfDays parameter is valid
- And: WhitelistedAmis parameter is configured
- And: The format of a WhitelistedAmis parameter element does not match the AMI ID format
- Then: Return value error
-
- Scenario 5:
- Given: NumberOfDays parameter is valid
- And: WhitelistedAmis parameter is valid
- And: WhitelistedInstances parameter is configured
- And: The format of a WhitelistedInstances parameter item does not match the Instance ID format
- Then: Return value error
-
- Scenario 6:
- Given: NumberOfDays parameter has not been configured
- And: WhitelistedAmis parameter is valid
- And: WhitelistedInstances parameter is valid
- And: The Instance AMI age is less than or equal to default value (90 days)
- Then: Return COMPLIANT
-
- Scenario 7:
- Given: NumberOfDays parameter has not been configured
- And: WhitelistedAmis parameter is valid
- And: Instance AMI ID is not contained in the WhitelistedAmis parameter
- And: WhitelistedInstances parameter is valid
- And: Instance ID is not contained in the WhitelistedInstances parameter
- And: The Instance AMI age is greater than default value (90 days)
- Then: Return NON_COMPLIANT
-
- Scenario 8:
- Given: NumberOfDays parameter has been configured
- And: NumberOfDays parameter is valid
- And: WhitelistedAmis parameter is valid
- And: Instance AMI ID is not contained in the WhitelistedAmis parameter
- And: WhitelistedInstances parameter is valid
- And: Instance ID is not contained in the WhitelistedInstances parameter
- And: The Instance AMI age is greater than configured value
- Then: Return NON_COMPLIANT
-
- Scenario 9:
- Given: NumberOfDays parameter has been configured
- And: NumberOfDays parameter is valid
- And: WhitelistedAmis parameter is valid
- And: WhitelistedInstances parameter is valid
- And: The Instance AMI age is less than or equal to than configured value
- Then: Return COMPLIANT
-
- Scenario 10:
- Given: NumberOfDays parameter is valid
- And: WhitelistedAmis parameter is valid
- And: WhitelistedInstances parameter is valid
- And: The Instance AMI ID is contained in the WhitelistedAmis parameter
- Then: Return COMPLIANT
-
- Scenario 11:
- Given: NumberOfDays parameter is valid
- And: WhitelistedAmis parameter is valid
- And: WhitelistedInstances parameter is valid
- And: The Instance ID is contained in the WhitelistedInstances parameter
- Then: Return COMPLIANT
-
-"""
-
-import json
-from datetime import datetime, timedelta
-import boto3
-import botocore
-from dateutil import parser
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::EC2::Instance'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service
-# (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated
- parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status,
- the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not
- returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a
- "shadow" evaluation to feedback that the evaluation took place properly
- """
- ec2_client = get_client('ec2', event)
- evaluations = []
-
- if configuration_item:
- ami_result = ec2_client.describe_images(
- ImageIds=[configuration_item['configuration']['imageId']]
- )
- print(ami_result)
- if ami_result['Images']:
- #print("AMI result:")
- #print(ami_result)
- status, annotation = evaluate_image(
- ami_result['Images'][0],
- configuration_item['configuration']['instanceId'],
- valid_rule_parameters
- )
- evaluations.append(
- build_evaluation_from_config_item(
- configuration_item,
- status,
- annotation=annotation
- )
- )
- else:
- #Scenario 1 : No Private AMI for the Instance
- evaluations.append(
- build_evaluation_from_config_item(
- configuration_item,
- "NOT_APPLICABLE",
- ""
- )
- )
- else:
- # First get all of the instances, paging through them if we have to.
- image_id_array = []
- instance_array = []
-
- instance_results = ec2_client.describe_instances()
- while True:
- for res in instance_results['Reservations']:
- for instance in res['Instances']:
- image_id_array.append(instance['ImageId'])
- instance_array.append(instance)
- if 'NextToken' in instance_results:
- next_token = instance_results['NextToken']
- instance_results = ec2_client.describe_instances(NextToken=next_token)
- else:
- break
- print(image_id_array)
- print(instance_array)
-
- # Use set() to get a list of just the unique image ID's.
- unique_image_ids = set(image_id_array)
-
- print(unique_image_ids)
-
- # Make as few API calls as possible to get the AMI data. A simpler loop in
- # which we calling the EC2 API for every instance would be easier, but would
- # result in a _lot_ more API activity and could cause throttling.
-
- # Create a lookup dict so that we can evaluate compliance for each instance.
- image_lookup = {}
-
- image_results = ec2_client.describe_images(
- ImageIds=list(unique_image_ids)
- )
- while True:
- for image in image_results['Images']:
- image_lookup[image['ImageId']] = image
-
- if 'NextToken' in image_results:
- next_token = image_results['NextToken']
- image_results = ec2_client.describe_images(
- ImageIds=list(unique_image_ids),
- NextToken=next_token
- )
- else:
- break
-
- print(image_lookup)
-
- # Now loop through the instances again and determine the compliance status,
- # appending it to our evaluations list.
- for instance in instance_array:
- if instance['ImageId'] in image_lookup:
- status, annotation = evaluate_image(
- image_lookup[instance['ImageId']],
- instance['InstanceId'],
- valid_rule_parameters
- )
- evaluations.append(
- build_evaluation(
- instance['InstanceId'],
- status,
- event,
- "AWS::EC2::Instance",
- annotation
- )
- )
- else:
- #Scenario 1 : No Private AMIs in the account then no resources in scope
- evaluations.append(
- build_evaluation(
- instance['InstanceId'],
- 'NOT_APPLICABLE',
- event,
- "AWS::EC2::Instance"
- )
- )
-
- return evaluations
-
-def evaluate_image(ami, instance_id, valid_rule_parameters):
- image_whitelist = valid_rule_parameters['WhitelistedAmis'].split(",")
-
- #Scenario 9 - Whitelisted AMI
- if ami['ImageId'] in image_whitelist:
- return 'COMPLIANT', "ImageId in AMI Whitelist"
-
- instance_whitelist = valid_rule_parameters['WhitelistedInstances'].split(",")
-
- #Scenario 10 - Whitelisted Instance
- if instance_id in instance_whitelist:
- return 'COMPLIANT', "InstanceId in Instance Whitelist"
-
- #Scenario 5-8
- # AMI age <= X days compliant.
- # AMI age > X days non-compliant.
-
- creation_date = parser.parse(ami['CreationDate'])
- current_date = datetime.now(creation_date.tzinfo)
- elapsed_time = timedelta(days=valid_rule_parameters['NumberOfDays'])
- expiration_date = creation_date + elapsed_time
-
- if current_date <= expiration_date:
- ann = "AMI is less than " + str(valid_rule_parameters['NumberOfDays']) + " days old."
- return 'COMPLIANT', ann
-
- ann = "The AMI is older than " + str(valid_rule_parameters['NumberOfDays']) + " days."
- return 'NON_COMPLIANT', ann
-
-def evaluate_parameters(rule_parameters):
- """
- Evaluate the rule parameters dictionary validity.
- Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- # Scenario 2: Validate NumberOfDays parameter.
- if 'NumberOfDays' not in rule_parameters:
- raise ValueError('The Config Rule must have the parameter "NumberOfDays"')
-
- if not rule_parameters['NumberOfDays']:
- rule_parameters['NumberOfDays'] = 90
-
- #The int() function will raise an error if the string configured can't be converted to an integer
- #no rounding, truncating, or modification.
- try:
- rule_parameters['NumberOfDays'] = int(rule_parameters['NumberOfDays'])
- except ValueError:
- raise ValueError('The parameter "NumberOfDays" must be a integer')
-
- if rule_parameters['NumberOfDays'] < 1:
- raise ValueError('The parameter "NumberOfDays" must be greater than 1')
-
- #Scenario 3: Validate WhitelistedAmis parameter
- if 'WhitelistedAmis' not in rule_parameters:
- raise ValueError(
- 'The Config Rule must have the parameter "WhitelistedAmis"'
- )
- else:
- if not isinstance(rule_parameters['WhitelistedAmis'], str):
- raise ValueError(
- 'WhitelistedAmis must be a string or a list of strings separated by comma.'
- )
- else:
- rule_parameters['WhitelistedAmis'] = rule_parameters['WhitelistedAmis'].replace(" ", "")
- image_whitelist = rule_parameters['WhitelistedAmis'].split(",")
- if image_whitelist[0]:
- for ami_id in image_whitelist:
- if not len(ami_id) >= 12:
- raise ValueError(
- 'The element "' + ami_id + '" in parameter "WhitelistedAmis" is not the correct length.'
- )
- if not ami_id.startswith('ami-'):
- raise ValueError(
- 'The element "' + ami_id + '" is not in the correct AMI ID format'
- )
-
- #Scenario 4: Validate WhitelistedInstances parameter
- if 'WhitelistedInstances' not in rule_parameters:
- raise ValueError('The Config Rule must have the parameter "WhitelistedInstances"')
- else:
- if not isinstance(rule_parameters['WhitelistedInstances'], str):
- raise ValueError(
- 'WhitelistedInstances must be a string or a list of strings separated by comma.'
- )
- else:
- rule_parameters['WhitelistedInstances'] = rule_parameters['WhitelistedInstances'].replace(" ", "")
- instance_whitelist = rule_parameters['WhitelistedInstances'].split(",")
- if instance_whitelist[0]:
- for instance_id in instance_whitelist:
- if not len(instance_id) >= 10:
- raise ValueError(
- 'The element "' + instance_id + '" in parameter "WhitelistedInstances" is not the correct length.'
- )
- if not instance_id.startswith('i-'):
- raise ValueError(
- 'The element "' + instance_id + '" is not in the correct AMI ID format'
- )
-
- return rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Parameter value is invalid",
- internalErrorDetails="An ValueError was raised during the validation of the Parameter value",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistory API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while(evaluation_copy):
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=resultToken, TestMode=testMode)
- del evaluation_copy[:100]
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/AMI_OUTDATED_CHECK/AMI_OUTDATED_CHECK_test.py b/vendor/github.com/awslabs/aws-config-rules/python/AMI_OUTDATED_CHECK/AMI_OUTDATED_CHECK_test.py
deleted file mode 100755
index a03bac5..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/AMI_OUTDATED_CHECK/AMI_OUTDATED_CHECK_test.py
+++ /dev/null
@@ -1,327 +0,0 @@
-import sys
-import unittest
-from datetime import datetime, timedelta
-from dateutil import parser
-import json
-
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::EC2::Instance'
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-ec2_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'sts':
- return sts_client_mock
- elif client_name == 'ec2':
- return ec2_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('AMI_OUTDATED_CHECK')
-
-class ComplianceTest(unittest.TestCase):
-
- #rule_parameters = '{"SomeParameterKey":"SomeParameterValue","SomeParameterKey2":"SomeParameterValue2"}'
-
- invoking_event_iam_role_sample = '{"configurationItem":{"relatedEvents":[],"relationships":[],"configuration":{},"tags":{},"configurationItemCaptureTime":"2018-07-02T03:37:52.418Z","awsAccountId":"123456789012","configurationItemStatus":"ResourceDiscovered","resourceType":"AWS::IAM::Role","resourceId":"some-resource-id","resourceName":"some-resource-name","ARN":"some-arn"},"notificationCreationTime":"2018-07-02T23:05:34.445Z","messageType":"ConfigurationItemChangeNotification"}'
- invoking_event_ec2_instance = '{"configurationItem":{"configuration":{"instanceId": "i-03402838daac1d611", "imageId": "ami-22ce4934", }, "awsAccountId":"123456789012", "configurationItemStatus":"ResourceDiscovered", "resourceType":"AWS::EC2::Instance", "resourceId":"i-03402838daac1d611", "resourceName":"some-resource-name", "ARN":"some-arn"}, "notificationCreationTime":"2018-07-02T23:05:34.445Z", "messageType":"ConfigurationItemChangeNotification"}'
-
- describe_instances_old_ami = {"Reservations": [{"Instances": [{"ImageId": "ami-12345678", "InstanceId": "i-12345678" }] }] }
- describe_images_old_ami = {"Images": [{"CreationDate": "2017-08-11T03:41:10.000Z","ImageId": "ami-12345678"}] }
-
- describe_instances_fresh_ami = {"Reservations": [{"Instances": [{"ImageId": "ami-87654321", "InstanceId": "i-87654321" }] }] }
- describe_images_fresh_ami = {"Images": [{"CreationDate": "2018-08-11T03:41:10.000Z","ImageId": "ami-87654321"}] }
-
- valid_params = '{"WhitelistedAmis": "", "WhitelistedInstances": "", "NumberOfDays": 60}'
- valid_params_default_days_old = '{"WhitelistedAmis": "", "WhitelistedInstances": "", "NumberOfDays": ""}'
- valid_params_whitelisted_image = '{"WhitelistedAmis": "ami-12345678", "WhitelistedInstances": "", "NumberOfDays": 60}'
- valid_params_whitelisted_instance = '{"WhitelistedAmis": "", "WhitelistedInstances": "i-12345678", "NumberOfDays": 60}'
- invalid_param_missing_image_whitelist = '{"WhitelistedInstances": "i-12345678", "NumberOfDays": 60}'
- invalid_param_missing_instance_whitelist = '{"WhitelistedAmis": "ami-045ceb7b", "NumberOfDays": 60}'
- invalid_param_missing_days_old = '{"WhitelistedAmis": "ami-045ceb7b", "WhitelistedInstances": "i-12345678"}'
- invalid_param_nonnumeric_days_old = '{"WhitelistedAmis": "ami-045ceb7b", "WhitelistedInstances": "i-12345678", "NumberOfDays": "sixty"}'
- invalid_param_days_old_too_low = '{"WhitelistedAmis": "ami-045ceb7b", "WhitelistedInstances": "i-12345678", "NumberOfDays": -60}'
- invalid_param_malformed_image_whitelist = '{"WhitelistedAmis": "image-2", "WhitelistedInstances": "i-12345678", "NumberOfDays": 60}'
- invalid_param_malformed_instance_whitelist = '{"WhitelistedAmis": "ami-045ceb7b", "WhitelistedInstances": "MyServer", "NumberOfDays": 60}'
-
- def setUp(self):
- #Set the creation_date for our "fresh image" describe_images mock call.
- old_creation_date = parser.parse(self.describe_images_fresh_ami['Images'][0]['CreationDate'])
- current_date = datetime.now(old_creation_date.tzinfo)
- elapsed_time = timedelta(days=1)
- new_creation_date = current_date - elapsed_time
- self.describe_images_fresh_ami['Images'][0]['CreationDate'] = new_creation_date.isoformat()
- print(new_creation_date)
-
- pass
-
- #Scenario 1
- def test_parameters_missing_image_whitelist(self):
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(self.invalid_param_missing_image_whitelist), {})
- assert_customer_error(self, response, "InvalidParameterValueException")
-
- def test_parameters_missing_instance_whitelist(self):
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(self.invalid_param_missing_instance_whitelist), {})
- assert_customer_error(self, response, "InvalidParameterValueException")
-
- def test_parameters_missing_days_old(self):
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(self.invalid_param_missing_days_old), {})
- assert_customer_error(self, response, "InvalidParameterValueException")
-
- #Scenario 2
- def test_parameters_nonnumeric_days_old(self):
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(self.invalid_param_nonnumeric_days_old), {})
- assert_customer_error(self, response, "InvalidParameterValueException")
-
- #Scenario 3
- def test_parameters_days_old_too_low(self):
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(self.invalid_param_days_old_too_low), {})
- assert_customer_error(self, response, "InvalidParameterValueException")
-
- #Scenario 4
- def test_parameters_malformed_ami_whitelist(self):
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(self.invalid_param_malformed_image_whitelist), {})
- assert_customer_error(self, response, "InvalidParameterValueException")
-
- #Scenario 5
- def test_parameters_malformed_instance_whitelist(self):
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(self.invalid_param_malformed_instance_whitelist), {})
- assert_customer_error(self, response, "InvalidParameterValueException")
-
- #Scenario 6
- def test_default_days_old_with_fresh_image(self):
- rule.ASSUME_ROLE_MODE = False
- ec2_client_mock.describe_instances = MagicMock(return_value=self.describe_instances_fresh_ami)
- ec2_client_mock.describe_images = MagicMock(return_value=self.describe_images_fresh_ami)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.valid_params_default_days_old), {})
- resp_expected = []
- resp_expected.append(
- build_expected_response(
- 'COMPLIANT',
- 'i-87654321',
- 'AWS::EC2::Instance',
- 'AMI is less than 90 days old.'))
-
- assert_successful_evaluation(self, response, resp_expected)
-
- #Scenario 7
- def test_default_days_old_with_old_image(self):
- rule.ASSUME_ROLE_MODE = False
- ec2_client_mock.describe_instances = MagicMock(return_value=self.describe_instances_old_ami)
- ec2_client_mock.describe_images = MagicMock(return_value=self.describe_images_old_ami)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.valid_params_default_days_old), {})
- resp_expected = []
- resp_expected.append(
- build_expected_response(
- 'NON_COMPLIANT',
- 'i-12345678',
- 'AWS::EC2::Instance',
- 'The AMI is older than 90 days.'))
-
- assert_successful_evaluation(self, response, resp_expected)
-
- #Scenario 8
- def test_specified_days_old_with_old_image(self):
- rule.ASSUME_ROLE_MODE = False
- ec2_client_mock.describe_instances = MagicMock(return_value=self.describe_instances_old_ami)
- ec2_client_mock.describe_images = MagicMock(return_value=self.describe_images_old_ami)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.valid_params), {})
- resp_expected = []
- resp_expected.append(
- build_expected_response(
- 'NON_COMPLIANT',
- 'i-12345678',
- 'AWS::EC2::Instance',
- 'The AMI is older than 60 days.'))
-
- assert_successful_evaluation(self, response, resp_expected)
-
- #Scenario 9
- def test_specified_days_old_with_fresh_image(self):
- rule.ASSUME_ROLE_MODE = False
- ec2_client_mock.describe_instances = MagicMock(return_value=self.describe_instances_fresh_ami)
- ec2_client_mock.describe_images = MagicMock(return_value=self.describe_images_fresh_ami)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.valid_params), {})
- resp_expected = []
- resp_expected.append(
- build_expected_response(
- 'COMPLIANT',
- 'i-87654321',
- 'AWS::EC2::Instance',
- 'AMI is less than 60 days old.'))
-
- assert_successful_evaluation(self, response, resp_expected)
-
- #Scenario 10
- def test_whitelisted_image(self):
- rule.ASSUME_ROLE_MODE = False
- ec2_client_mock.describe_instances = MagicMock(return_value=self.describe_instances_old_ami)
- ec2_client_mock.describe_images = MagicMock(return_value=self.describe_images_old_ami)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.valid_params_whitelisted_image), {})
- resp_expected = []
- resp_expected.append(
- build_expected_response(
- 'COMPLIANT',
- 'i-12345678',
- 'AWS::EC2::Instance',
- 'ImageId in AMI Whitelist'))
-
- assert_successful_evaluation(self, response, resp_expected)
-
- #Scenario 11
- def test_whitelisted_instance(self):
- rule.ASSUME_ROLE_MODE = False
- ec2_client_mock.describe_instances = MagicMock(return_value=self.describe_instances_old_ami)
- ec2_client_mock.describe_images = MagicMock(return_value=self.describe_images_old_ami)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.valid_params_whitelisted_instance), {})
- resp_expected = []
- resp_expected.append(
- build_expected_response(
- 'COMPLIANT',
- 'i-12345678',
- 'AWS::EC2::Instance',
- 'InstanceId in Instance Whitelist'))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-#Use this one if we don't needto validate the exact error verbiage.
-def assert_customer_error(testClass, response, customerErrorCode):
- assert_customer_error_response(testClass, response, customerErrorCode, response["customerErrorMessage"])
-
-def assert_customer_error_response(testClass, response, customerErrorCode, customerErrorMessage):
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- sts_client_mock.reset_mock(return_value=True)
- sts_client_mock.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
- invoking_event_iam_role_sample = '{"configurationItem":{"relatedEvents":[],"relationships":[],"configuration":{},"tags":{},"configurationItemCaptureTime":"2018-07-02T03:37:52.418Z","awsAccountId":"123456789012","configurationItemStatus":"ResourceDiscovered","resourceType":"AWS::IAM::Role","resourceId":"some-resource-id","resourceName":"some-resource-name","ARN":"some-arn"},"notificationCreationTime":"2018-07-02T23:05:34.445Z","messageType":"ConfigurationItemChangeNotification"}'
- valid_params = '{"WhitelistedAmis": "", "WhitelistedInstances": "", "NumberOfDays": 60}'
- def test_sts_unknown_error(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event(self.invoking_event_iam_role_sample, self.valid_params), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event(self.invoking_event_iam_role_sample, self.valid_params), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/AMI_OUTDATED_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/AMI_OUTDATED_CHECK/parameters.json
deleted file mode 100644
index 8ddbcd1..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/AMI_OUTDATED_CHECK/parameters.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "CodeKey": "AMI_OUTDATED_CHECK.zip",
- "SourceRuntime": "python3.6",
- "RuleName": "AMI_OUTDATED_CHECK",
- "SourcePeriodic": "TwentyFour_Hours",
- "OptionalParameters": "{\"WhitelistedAmis\": \" \", \"WhitelistedInstances\": \" \", \"NumberOfDays\": \"60\"}",
- "InputParameters": "{}"
- }
-}
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_CACHE_ENABLED_AND_ENCRYPTED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/API_GW_CACHE_ENABLED_AND_ENCRYPTED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_CACHE_ENABLED_AND_ENCRYPTED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_CACHE_ENABLED_AND_ENCRYPTED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/API_GW_CACHE_ENABLED_AND_ENCRYPTED/parameters.json
deleted file mode 100644
index bde27fb..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_CACHE_ENABLED_AND_ENCRYPTED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "API_GW_CACHE_ENABLED_AND_ENCRYPTED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::ApiGateway::Stage",
- "SourceIdentifier": "API_GW_CACHE_ENABLED_AND_ENCRYPTED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_ENDPOINT_TYPE_CHECK/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/API_GW_ENDPOINT_TYPE_CHECK/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_ENDPOINT_TYPE_CHECK/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_ENDPOINT_TYPE_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/API_GW_ENDPOINT_TYPE_CHECK/parameters.json
deleted file mode 100644
index f3c1201..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_ENDPOINT_TYPE_CHECK/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "API_GW_ENDPOINT_TYPE_CHECK",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{\"endpointConfigurationTypes\": \"\"}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::ApiGateway::RestApi",
- "SourceIdentifier": "API_GW_ENDPOINT_TYPE_CHECK"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_EXECUTION_LOGGING_ENABLED/API_GW_EXECUTION_LOGGING_ENABLED.py b/vendor/github.com/awslabs/aws-config-rules/python/API_GW_EXECUTION_LOGGING_ENABLED/API_GW_EXECUTION_LOGGING_ENABLED.py
deleted file mode 100644
index 16fa736..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_EXECUTION_LOGGING_ENABLED/API_GW_EXECUTION_LOGGING_ENABLED.py
+++ /dev/null
@@ -1,361 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-'''
-#####################################
-## Gherkin ##
-#####################################
-Rule Name:
- API_GW_EXECUTION_LOGGING_ENABLED
-Description:
- Checks that methods in an Amazon API Gateway stage for deployed APIs have 'loggingLevel' as one of the values specified in the rule parameter 'loggingLevel'. The rule returns NON_COMPLIANT if any method in a stage has 'loggingLevel' set to a value not matching any of the logging levels specified in the rule parameter.
-Trigger:
- Configuration Change on AWS::ApiGateway::Stage or AWS::ApiGatewayV2::Stage
-Reports on:
- AWS::ApiGateway::Stage or AWS::ApiGatewayV2::Stage
-Rule Parameters:
- loggingLevel
- (Optional) Comma-separated list of allowed logging levels. Default is "ERROR,INFO"
-Scenarios:
- Scenario: 1
- Given: The rule parameter is invalid
- Then: Return ERROR
- Scenario: 2
- Given: The rule parameter is valid
- And: In the Stage configuration item, at least one method in 'methodSettings' has the 'loggingLevel' set to a value not in rule parameter.
- Then: Return NON_COMPLIANT
- Scenario: 3
- Given: The rule parameter is valid
- And: In the Stage configuration item, all methods in 'methodSettings' have the 'loggingLevel' set to a value in rule parameter.
- Then: Return COMPLIANT
-'''
-
-import json
-import sys
-import datetime
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::ApiGateway::Stage'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-ALLOWED_LOGGING_LEVEL_VALUES = ["ERROR", "INFO"]
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- stage = configuration_item['configuration']
- methods_logging_not_enabled = []
-
- #Resource Type AWS::ApiGateway::Stage
- if configuration_item['resourceType'] == 'AWS::ApiGateway::Stage':
- if stage["methodSettings"]:
- for method in stage["methodSettings"]:
- if stage["methodSettings"][method]["loggingLevel"] not in valid_rule_parameters:
- methods_logging_not_enabled.append(method)
- if methods_logging_not_enabled:
- #Scenario 2: If at least one method in 'methodSettings' has the 'loggingLevel' set to a value not in rule parameter return non_compliant.
- return build_evaluation_from_config_item(configuration_item, 'NON_COMPLIANT', 'Logging Level does not match the value for rule parameter (loggingLevel): ' + str(valid_rule_parameters) + ' in this Amazon API Gateway Stage for the following method(s): [' + ', '.join(methods_logging_not_enabled) + '].')
- #Scenario 3: If all methods in 'methodSettings' have the 'loggingLevel' set to a value in rule parameter return compliant.
- return build_evaluation_from_config_item(configuration_item, 'COMPLIANT')
- return build_evaluation_from_config_item(configuration_item, 'NON_COMPLIANT', 'Logging is not configured for this Amazon API Gateway Stage.')
-
- #Resource Type AWS::ApiGatewayV2::Stage
- if stage["defaultRouteSettings"]["loggingLevel"] in valid_rule_parameters:
- #Scenario 3: If all methods in 'methodSettings' have the 'loggingLevel' set to a value in rule parameter return compliant.
- return build_evaluation_from_config_item(configuration_item, 'COMPLIANT')
- #Scenario 2: If at least one method in 'methodSettings' has the 'loggingLevel' set to a value not in rule parameter return non_compliant.
- return build_evaluation_from_config_item(configuration_item, 'NON_COMPLIANT', 'Logging Level does not match the value for rule parameter (loggingLevel): ' + str(valid_rule_parameters) + ' in this Amazon API Gateway Stage.')
-
-def evaluate_parameters(rule_parameters):
- valid_rule_parameters = ALLOWED_LOGGING_LEVEL_VALUES
- #Scenario 1: The rule parameter is invalid
- if "loggingLevel" in rule_parameters:
- list_rule_parameters = [rule_parameter.strip() for rule_parameter in rule_parameters['loggingLevel'].split(',')]
- for each_parameter in list_rule_parameters:
- if each_parameter not in ALLOWED_LOGGING_LEVEL_VALUES:
- raise ValueError('Logging Level: ' + each_parameter + ' is not a valid logging level.')
- valid_rule_parameters = list_rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-def build_parameters_value_error_response(ex):
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-def get_client(service, event):
-
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return status in ("OK", "ResourceDiscovered") and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_EXECUTION_LOGGING_ENABLED/API_GW_EXECUTION_LOGGING_ENABLED_test.py b/vendor/github.com/awslabs/aws-config-rules/python/API_GW_EXECUTION_LOGGING_ENABLED/API_GW_EXECUTION_LOGGING_ENABLED_test.py
deleted file mode 100644
index e02bd51..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_EXECUTION_LOGGING_ENABLED/API_GW_EXECUTION_LOGGING_ENABLED_test.py
+++ /dev/null
@@ -1,370 +0,0 @@
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import json
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::ApiGateway::RestApi'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('API_GW_EXECUTION_LOGGING_ENABLED')
-
-class ParameterTest(unittest.TestCase):
-
- stage_settings_1 = {
- 'methodSettings':{
- '*/*':{
- 'loggingLevel':'OFF'
- }
- }
- }
- stage_settings_2 = {
- 'methodSettings':{
- '*/*':{
- 'loggingLevel':'INFO'
- }
- }
- }
-
- #Scenario 1: Rule Parameter is invalid
- def test_invalid_param_value(self):
- rule_parameters = '{"loggingLevel": "INVALID"}'
- invoking_event = build_invoking_event(self.stage_settings_1, 'AWS::ApiGateway::Stage', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/test')
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event, rule_parameters), {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
- def test_invalid_parameter_values2(self):
- rule_parameters = '{"loggingLevel": "ERROR,NOTSET"}'
- invoking_event = build_invoking_event(self.stage_settings_1, 'AWS::ApiGateway::Stage', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/test')
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event, rule_parameters), {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
- #Scenario 2: Non Compliant
- def test_no_parameter_non_compliant(self):
- rule_parameters = '{}'
- invoking_event = build_invoking_event(self.stage_settings_1, 'AWS::ApiGateway::Stage', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/test')
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event, rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/test', 'AWS::ApiGateway::Stage', 'Logging Level does not match the value for rule parameter (loggingLevel): [\'ERROR\', \'INFO\'] in this Amazon API Gateway Stage for the following method(s): [*/*].'))
- assert_successful_evaluation(self, response, resp_expected)
-
- #Scenario 3: Compliant
- def test_no_parameter_compliant(self):
- rule_parameters = '{}'
- invoking_event = build_invoking_event(self.stage_settings_2, 'AWS::ApiGateway::Stage', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/test')
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event, rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/test', 'AWS::ApiGateway::Stage'))
- assert_successful_evaluation(self, response, resp_expected)
-
-class LoggingLevelTest(unittest.TestCase):
- rule_parameters = '{"loggingLevel": "ERROR,INFO"}'
-
- #Scenario 2: Non compliant for Resource Type AWS::ApiGateway::Stage
- def test_logging_level_default_off(self):
- stage_settings = {
- 'methodSettings':{}
- }
- invoking_event = build_invoking_event(stage_settings, 'AWS::ApiGateway::Stage', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/limit')
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event, self.rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/limit', 'AWS::ApiGateway::Stage', 'Logging is not configured for this Amazon API Gateway Stage.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_logging_level_overriden_off(self):
- stage_settings = {
- 'methodSettings':{
- '~1test~1{proxy}/GET':{
- 'loggingLevel':'OFF'
- },
- '*/*':{
- 'loggingLevel':'INFO'
- }
- }
- }
- invoking_event = build_invoking_event(stage_settings, 'AWS::ApiGateway::Stage', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/limit')
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event, self.rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/limit', 'AWS::ApiGateway::Stage', 'Logging Level does not match the value for rule parameter (loggingLevel): [\'ERROR\', \'INFO\'] in this Amazon API Gateway Stage for the following method(s): [~1test~1{proxy}/GET].'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_logging_level_info_non_compliant(self):
- stage_settings = {
- 'methodSettings':{
- '*/*':{
- 'loggingLevel':'INFO'
- }
- }
- }
- invoking_event = build_invoking_event(stage_settings, 'AWS::ApiGateway::Stage', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/test')
- rule_parameters = '{"loggingLevel": "ERROR"}'
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event, rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/test', 'AWS::ApiGateway::Stage', 'Logging Level does not match the value for rule parameter (loggingLevel): [\'ERROR\'] in this Amazon API Gateway Stage for the following method(s): [*/*].'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_logging_level_error_non_compliant(self):
- stage_settings = {
- 'methodSettings':{
- '*/*':{
- 'loggingLevel':'ERROR'
- }
- }
- }
- invoking_event = build_invoking_event(stage_settings, 'AWS::ApiGateway::Stage', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/test')
- rule_parameters = '{"loggingLevel": "INFO"}'
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event, rule_parameters), {})
- print(response)
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/test', 'AWS::ApiGateway::Stage', 'Logging Level does not match the value for rule parameter (loggingLevel): [\'INFO\'] in this Amazon API Gateway Stage for the following method(s): [*/*].'))
- assert_successful_evaluation(self, response, resp_expected)
-
- #Scenario 3: Compliant for Resource Type AWS::ApiGateway::Stage
- def test_logging_level_info(self):
- stage_settings = {
- 'methodSettings':{
- '*/*':{
- 'loggingLevel':'INFO'
- }
- }
- }
- invoking_event = build_invoking_event(stage_settings, 'AWS::ApiGateway::Stage', 'arn:aws:apigateway:us-east-1::/restapis/asdf567gh/stages/test')
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event, self.rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'arn:aws:apigateway:us-east-1::/restapis/asdf567gh/stages/test', 'AWS::ApiGateway::Stage'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_logging_level_error(self):
- stage_settings = {
- 'methodSettings':{
- '*/*':{
- 'loggingLevel':'ERROR'
- }
- }
- }
- invoking_event = build_invoking_event(stage_settings, 'AWS::ApiGateway::Stage', 'arn:aws:apigateway:us-east-1::/restapis/asdf567gh/stages/test')
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event, self.rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'arn:aws:apigateway:us-east-1::/restapis/asdf567gh/stages/test', 'AWS::ApiGateway::Stage'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_logging_level_info_compliant(self):
- stage_settings = {
- 'methodSettings':{
- '~1test~1{proxy}/GET':{
- 'loggingLevel':'INFO'
- },
- '*/*':{
- 'loggingLevel':'INFO'
- }
- }
- }
- invoking_event = build_invoking_event(stage_settings, 'AWS::ApiGateway::Stage', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/test')
- rule_parameters = '{"loggingLevel": "INFO"}'
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event, rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/test', 'AWS::ApiGateway::Stage'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_logging_level_error_compliant(self):
- stage_settings = {
- 'methodSettings':{
- '*/*':{
- 'loggingLevel':'ERROR'
- }
- }
- }
- invoking_event = build_invoking_event(stage_settings, 'AWS::ApiGateway::Stage', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/test')
- rule_parameters = '{"loggingLevel": "ERROR"}'
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event, rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'arn:aws:apigateway:us-east-1::/restapis/abcd123fgh/stages/test', 'AWS::ApiGateway::Stage'))
- assert_successful_evaluation(self, response, resp_expected)
-
- #Scenario 2: Non Compliant for Resource Type AWS::ApiGatewayV2::Stage
- def test_apiv2_change_loglevel_to_off(self):
- stage_settings = {
- 'defaultRouteSettings':{
- 'loggingLevel':'OFF'
- },
- }
- RULE.ASSUME_ROLE_MODE = False
- invoking_event = build_invoking_event(stage_settings, 'AWS::ApiGatewayV2::Stage', 'arn:aws:apigateway:us-east-1::/apis/qwert123yu/stages/test')
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event, self.rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'arn:aws:apigateway:us-east-1::/apis/qwert123yu/stages/test', 'AWS::ApiGatewayV2::Stage', 'Logging Level does not match the value for rule parameter (loggingLevel): [\'ERROR\', \'INFO\'] in this Amazon API Gateway Stage.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- #Scenario 3: Compliant for Resource Type AWS::ApiGatewayV2::Stage
- def test_apiv2_change_loglevel_to_info(self):
- stage_settings = {
- 'defaultRouteSettings':{
- 'loggingLevel':'INFO'
- }
- }
- invoking_event = build_invoking_event(stage_settings, 'AWS::ApiGatewayV2::Stage', 'arn:aws:apigateway:us-east-1::/apis/qwert123yu/stages/test')
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event, self.rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'arn:aws:apigateway:us-east-1::/apis/qwert123yu/stages/test', 'AWS::ApiGatewayV2::Stage'))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_invoking_event(stage_settings, resource_type, resource_id):
- invoking_event_to_return = {
- 'configurationItem':{
- 'configuration':stage_settings,
- 'configurationItemStatus':'OK',
- 'configurationItemCaptureTime':'2019-04-13T17:18:21.693Z',
- 'resourceType':resource_type,
- 'resourceId':resource_id,
- 'resourceName':'test',
- 'ARN':resource_id
- },
- 'messageType':'ConfigurationItemChangeNotification'
- }
- return json.dumps(invoking_event_to_return)
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_EXECUTION_LOGGING_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/API_GW_EXECUTION_LOGGING_ENABLED/parameters.json
deleted file mode 100644
index 24f97c3..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_EXECUTION_LOGGING_ENABLED/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "API_GW_EXECUTION_LOGGING_ENABLED",
- "SourceRuntime": "python3.6",
- "CodeKey": "API_GW_EXECUTION_LOGGING_ENABLED.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{\"loggingLevel\": \"\"}",
- "SourceEvents": "AWS::ApiGateway::Stage,AWS::ApiGatewayV2::Stage"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_NOT_EDGE_OPTIMISED/API_GW_NOT_EDGE_OPTIMISED.py b/vendor/github.com/awslabs/aws-config-rules/python/API_GW_NOT_EDGE_OPTIMISED/API_GW_NOT_EDGE_OPTIMISED.py
deleted file mode 100644
index d39ab6f..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_NOT_EDGE_OPTIMISED/API_GW_NOT_EDGE_OPTIMISED.py
+++ /dev/null
@@ -1,451 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-'''
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- api-gw-is-not-edge-optimised
-
-Description:
- Check that all APIs are private or regional, and not edge optimised.
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::ApiGateway::RestApi
-
-Parameters:
- | ----------------------|-----------|-----------------------------------------------|
- | Parameter Name | Type | Description |
- | ----------------------|-----------|---------------------------------------------- |
- | ExceptionList | Optional | List the ID of allowed EDGE API Gateways, |
- | | | separated by a comma. |
- |-----------------------|-----------|-----------------------------------------------|
-
-Feature:
- In order to: to limit the access to API
- As: a Security Officer
- I want: To ensure that all APIs in API GW are private or regional.
-
-Scenarios:
- Scenario 1:
- Given: the ExceptionList parameter is not alphanumerical lower case
- Then: return an Error
-
- Scenario 2:
- Given: an API is in the ExceptionList
- Then: return COMPLIANT with annotation "API is part of exception list"
-
- Scenario 3:
- Given: an API is not in the ExceptionList
- And: API has endpointConfiguration including the type EDGE
- Then: return NON_COMPLIANT
-
- Scenario 4:
- Given: the API is not in the ExceptionList
- And: API has endpointConfiguration do not include the type EDGE
- Then: return COMPLIANT
-
-Note: Since endpointConfiguration Type is a list, to anticipate potential new type or multi-type APIs, we do not restrict on unique value or on EDGE.
-'''
-
-
-import json
-import datetime
-import re
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::ApiGateway::RestApi'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#The API Gateway Type that shouldn't be present in the account
-API_TYPE = 'EDGE'
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
-
- apigw_client = get_client('apigateway', event)
- gateways_list = get_all_api_gateway(apigw_client)
-
- if not gateways_list:
- return None
-
- evaluations = []
- for gateway in gateways_list:
-
- # SCENARIO 2: API is in exception list.
- if gateway['id'] in valid_rule_parameters:
- evaluations.append(build_evaluation(gateway['id'], 'COMPLIANT', event, annotation='API is part of exception list.'))
- continue
-
- # SCENARIO 3: EDGE Optimised API is present.
- if API_TYPE in gateway['endpointConfiguration']['types']:
- evaluations.append(build_evaluation(gateway['id'], 'NON_COMPLIANT', event, annotation='EDGE OPTIMIZED API Gateway is present.'))
- continue
-
- # SCENARIO 4: EDGE Optimised API is not present.
- evaluations.append(build_evaluation(gateway['id'], 'COMPLIANT', event))
-
- return evaluations
-
-def get_all_api_gateway(client):
- rest_apis_list = client.get_rest_apis(limit=500)
- apis_list = []
- while True:
- for item in rest_apis_list['items']:
- apis_list.append(item)
- if 'position' in rest_apis_list:
- rest_apis_list = client.get_rest_apis(position=rest_apis_list['position'], limit=500)
- else:
- break
- return apis_list
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- if not rule_parameters:
- return {}
-
- if not 'ExceptionList' in rule_parameters:
- return {}
-
- excepted_apis = rule_parameters['ExceptionList'].replace(' ', '').split(',')
- valid_apis = []
-
- # SCENARIO 1: Parameter is invalid.
- for excepted_api in excepted_apis:
- if not re.match("[a-z0-9]+$", excepted_api):
- raise ValueError('Invalid value in the ExceptionList: '+ excepted_api)
- valid_apis.append(excepted_api)
-
- return valid_apis
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Parameter value is invalid",
- internalErrorDetails="An ValueError was raised during the validation of the Parameter value",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while(evaluation_copy):
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=resultToken, TestMode=testMode)
- del evaluation_copy[:100]
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_NOT_EDGE_OPTIMISED/API_GW_NOT_EDGE_OPTIMISED_test.py b/vendor/github.com/awslabs/aws-config-rules/python/API_GW_NOT_EDGE_OPTIMISED/API_GW_NOT_EDGE_OPTIMISED_test.py
deleted file mode 100644
index 1a4b097..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_NOT_EDGE_OPTIMISED/API_GW_NOT_EDGE_OPTIMISED_test.py
+++ /dev/null
@@ -1,277 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::ApiGateway::RestApi'
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-apigw_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'sts':
- return sts_client_mock
- elif client_name == 'apigateway':
- return apigw_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('API_GW_NOT_EDGE_OPTIMISED')
-
-class ParameterTest(unittest.TestCase):
- get_rest_apis_private = {
- 'items': [{'id': 'apiid1', 'endpointConfiguration': {'types': ['PRIVATE']}},
- {'id': 'apiid2', 'endpointConfiguration': {'types': ['PRIVATE']}}]
- }
-
- invalid_rule_parameters = '{"ExceptionList":"apiid-1"}'
-
- def test_api_invalid_parameter(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.get_rest_apis_private)
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=self.invalid_rule_parameters), {})
- assert_customer_error_response(
- self, response, 'InvalidParameterValueException', 'Invalid value in the ExceptionList: apiid-1')
-
-class ComplianceTest(unittest.TestCase):
-
- rule_parameters = '{"ExceptionList":"apiid1,apiid2"}'
-
- invoking_event_iam_role_sample = '{"configurationItem":{"relatedEvents":[],"relationships":[],"configuration":{},"tags":{},"configurationItemCaptureTime":"2018-07-02T03:37:52.418Z","awsAccountId":"123456789012","configurationItemStatus":"ResourceDiscovered","resourceType":"AWS::IAM::Role","resourceId":"some-resource-id","resourceName":"some-resource-name","ARN":"some-arn"},"notificationCreationTime":"2018-07-02T23:05:34.445Z","messageType":"ConfigurationItemChangeNotification"}'
-
- get_rest_apis_private = {
- 'items': [{'id': 'apiid1', 'endpointConfiguration': {'types': ['PRIVATE']}},
- {'id': 'apiid2', 'endpointConfiguration': {'types': ['PRIVATE']}}]
- }
-
- get_rest_apis_regional = {
- 'items': [{'id': 'apiid1', 'endpointConfiguration': {'types': ['REGIONAL']}},
- {'id': 'apiid2', 'endpointConfiguration': {'types': ['REGIONAL']}}]
- }
-
- get_rest_apis_edge = {
- 'items': [{'id': 'apiid1', 'endpointConfiguration': {'types': ['EDGE']}},
- {'id': 'apiid2', 'endpointConfiguration': {'types': ['EDGE']}}]
- }
-
- get_rest_apis_mix_compliant_only = {
- 'items': [{'id': 'apiid1', 'endpointConfiguration': {'types': ['REGIONAL']}},
- {'id': 'apiid2', 'endpointConfiguration': {'types': ['PRIVATE']}}]
- }
-
- get_rest_apis_mix = {
- 'items': [{'id': 'apiid1', 'endpointConfiguration': {'types': ['EDGE']}},
- {'id': 'apiid2', 'endpointConfiguration': {'types': ['REGIONAL']}},
- {'id': 'apiid3', 'endpointConfiguration': {'types': ['PRIVATE']}}]
- }
-
- get_rest_apis_multi_type = {
- 'items': [{'id': 'apiid1', 'endpointConfiguration': {'types': ['EDGE', 'PRIVATE']}},
- {'id': 'apiid2', 'endpointConfiguration': {'types': ['REGIONAL']}}]
- }
-
- def test_no_gw(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value={"items": []})
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_private_only_COMPLIANT(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.get_rest_apis_private)
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'apiid1'))
- resp_expected.append(build_expected_response('COMPLIANT', 'apiid2'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- def test_regional_only_COMPLIANT(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.get_rest_apis_regional)
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'apiid1'))
- resp_expected.append(build_expected_response('COMPLIANT', 'apiid2'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- def test_edge_only_NON_COMPLIANT(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.get_rest_apis_edge)
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'apiid1', annotation="EDGE OPTIMIZED API Gateway is present."))
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'apiid2', annotation="EDGE OPTIMIZED API Gateway is present."))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- def test_mix_COMPLIANT(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.get_rest_apis_mix_compliant_only)
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'apiid1'))
- resp_expected.append(build_expected_response('COMPLIANT', 'apiid2'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- def test_mix(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.get_rest_apis_mix)
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'apiid1', annotation="EDGE OPTIMIZED API Gateway is present."))
- resp_expected.append(build_expected_response('COMPLIANT', 'apiid2'))
- resp_expected.append(build_expected_response('COMPLIANT', 'apiid3'))
- assert_successful_evaluation(self, response, resp_expected, 3)
-
- def test_edge_exception_COMPLIANT(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.get_rest_apis_edge)
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=self.rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'apiid1', annotation="API is part of exception list."))
- resp_expected.append(build_expected_response('COMPLIANT', 'apiid2', annotation="API is part of exception list."))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- def test_mix_with_exceptions(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.get_rest_apis_mix)
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=self.rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'apiid1', annotation="API is part of exception list."))
- resp_expected.append(build_expected_response('COMPLIANT', 'apiid2', annotation="API is part of exception list."))
- resp_expected.append(build_expected_response('COMPLIANT', 'apiid3'))
- assert_successful_evaluation(self, response, resp_expected, 3)
-
- def test_multi_type(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.get_rest_apis_multi_type)
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'apiid1', annotation="EDGE OPTIMIZED API Gateway is present."))
- resp_expected.append(build_expected_response('COMPLIANT', 'apiid2'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(testClass, response, customerErrorCode=None, customerErrorMessage=None):
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- sts_client_mock.reset_mock(return_value=True)
- sts_client_mock.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_NOT_EDGE_OPTIMISED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/API_GW_NOT_EDGE_OPTIMISED/parameters.json
deleted file mode 100644
index 234fd66..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_NOT_EDGE_OPTIMISED/parameters.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "Parameters": {
- "RuleName": "API_GW_NOT_EDGE_OPTIMISED",
- "SourceRuntime": "python3.6",
- "CodeKey": "API_GW_NOT_EDGE_OPTIMISED.zip",
- "InputParameters": "{\"ExceptionList\":\"somegatewayid\"}",
- "SourcePeriodic": "TwentyFour_Hours"
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_PRIVATE_RESTRICTED/API_GW_PRIVATE_RESTRICTED.py b/vendor/github.com/awslabs/aws-config-rules/python/API_GW_PRIVATE_RESTRICTED/API_GW_PRIVATE_RESTRICTED.py
deleted file mode 100644
index 2dcee27..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_PRIVATE_RESTRICTED/API_GW_PRIVATE_RESTRICTED.py
+++ /dev/null
@@ -1,551 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-'''
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- api-gw-api-private-restricted.
-
-Description:
- Check that all private APIs uses resource policy restricting to VPC endpoints or VPC in the same AWS account.
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::ApiGateway::RestApi
-
-Parameters:
- None
-
-Feature:
- In order to: to limit the access to API
- As: a Security Officer
- I want: To ensure that all private APIs in API GW have a resource based policy which limit their usage to a VPC or VPC Endpoint based in the same account.
-
-Scenarios:
- Scenario 1:
- Given: an API is not in Private mode
- Then: return NOT_APPLICABLE
-
- Scenario 2:
- Given: an API is in Private mode
- And: this API does not have a resource policy attached
- Then: return NON_COMPLIANT
-
- Scenario 3:
- Given: an API is in Private mode
- And: this API has no resource policy with an 'Allow' statement
- Then: return NON_COMPLIANT
-
- Scenario 4:
- Given: an API is in Private mode
- And: APIs have resource policy attached, containing one or more 'Allow' statements
- And: The 'Allow' statement(s) does not contain
- Then: return NON_COMPLIANT
-
- With:
- | Options of Policy |
- | any 'Condition' |
- | any 'Condition' about 'StringEquals' |
- | any 'Condition' about 'StringEquals' about 'aws:sourceVpce' |
- | any 'Condition' about 'StringEquals' about 'aws:sourceVpc' |
-
- Scenario 5:
- Given: an API is in Private mode
- And: APIs have resource policy attached, containing one or more 'Allow' statements
- And: The 'Allow' statement(s) contain a 'Condition' about 'StringEquals' about 'aws:sourceVpc'
- And: Those VPCs are not in the same account than this API Gateway.
- Then: return NON_COMPLIANT
-
- Scenario 6:
- Given: an API is in Private mode
- And: APIs have resource policy attached, containing one or more 'Allow' statements
- And: The 'Allow' statement(s) contain a 'Condition' about 'StringEquals' about 'aws:sourceVpc'
- And: Those VPCs are in the same account than this API Gateway.
- Then: return COMPLIANT
-
- Scenario 7:
- Given: an API is in Private mode
- And: APIs have resource policy attached, containing one or more 'Allow' statements
- And: The 'Allow' statement(s) contain a 'Condition' about 'StringEquals' about 'aws:sourceVpce'
- And: Those VPC Endpoints are not in the same account than this API Gateway.
- Then: return NON_COMPLIANT
-
- Scenario 8:
- Given: an API is in Private mode
- And: APIs have resource policy attached, containing one or more 'Allow' statements
- And: The 'Allow' statement(s) contain a 'Condition' about 'StringEquals' about 'aws:sourceVpce'
- And: Those VPC Endpoints are in the same account than this API Gateway.
- Then: return COMPLIANT
-
-'''
-
-import json
-import datetime
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::ApiGateway::RestApi'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
-
- apigw_client = get_client('apigateway', event)
- gateways_list = get_all_api_gateway(apigw_client)
-
- if not gateways_list:
- return None
-
- ec2_client = get_client('ec2', event)
- all_vpc_in_account = ec2_client.describe_vpcs()
- all_vpce_in_account = get_all_vpce(ec2_client)
-
- evaluations = []
- for gateway in gateways_list:
-
- #Scenario 1:
- if 'PRIVATE' not in gateway['endpointConfiguration']['types']:
- evaluations.append(build_evaluation(gateway['name'], 'NOT_APPLICABLE', event))
- continue
-
- #Scenario 2:
- if 'policy' not in gateway:
- evaluations.append(build_evaluation(gateway['name'], 'NON_COMPLIANT', event, annotation='No resource policy is attached.'))
- continue
-
- policy = json.loads(gateway['policy'].replace('\\', ''))
-
- policy_has_allow_statement = False
- is_gateway_compliant = True
-
- for statement in policy['Statement']:
- if statement['Effect'] == 'Allow':
- policy_has_allow_statement = True
-
- if not allow_statement_has_options(statement):
- evaluations.append(build_evaluation(gateway['name'], 'NON_COMPLIANT', event, annotation='The Allow statement does not have VPC nor a VPCe.'))
- is_gateway_compliant = False
- break
-
- if allow_statement_has_attrib(statement, 'aws:sourceVpc'):
- vpc_list = statement['Condition']['StringEquals']['aws:sourceVpc']
- if not is_resource_in_same_account(vpc_list, 'VpcId', all_vpc_in_account['Vpcs']):
- evaluations.append(build_evaluation(gateway['name'], 'NON_COMPLIANT', event, annotation='The VPCs are not in the same account than this API Gateway.'))
- is_gateway_compliant = False
- break
-
- if allow_statement_has_attrib(statement, 'aws:sourceVpce'):
- vpce_list = statement['Condition']['StringEquals']['aws:sourceVpce']
- if not is_resource_in_same_account(vpce_list, 'VpcEndpointId', all_vpce_in_account):
- evaluations.append(build_evaluation(gateway['name'], 'NON_COMPLIANT', event, annotation='The VPCEs are not in the same account than this API Gateway.'))
- is_gateway_compliant = False
- break
-
- if not policy_has_allow_statement:
- evaluations.append(build_evaluation(gateway['name'], 'NON_COMPLIANT', event, annotation='This API has no resource policy with an Allow statement.'))
- is_gateway_compliant = False
- continue
-
- if is_gateway_compliant:
- evaluations.append(build_evaluation(gateway['name'], 'COMPLIANT', event))
-
- return evaluations
-
-def allow_statement_has_options(statement):
- if not 'Condition' in statement:
- return False
- if not 'StringEquals' in statement['Condition']:
- return False
- if not allow_statement_has_attrib(statement, 'aws:sourceVpc') and not allow_statement_has_attrib(statement, 'aws:sourceVpce'):
- return False
- return True
-
-def allow_statement_has_attrib(statement, attrib_value):
- if not 'Condition' in statement:
- return False
- if not 'StringEquals' in statement['Condition']:
- return False
- if not attrib_value in statement['Condition']['StringEquals']:
- return False
- return True
-
-def is_resource_in_same_account(resource, key_id, all_resources_in_account):
- resource_list = []
- if not isinstance(resource, list):
- resource_list.append(resource)
- else:
- resource_list = resource
-
- if not resource_list:
- return True
-
- for current_resource in resource_list:
- if not in_dictlist(key_id, str(current_resource), all_resources_in_account):
- return False
- return True
-
-def in_dictlist(key, value, my_dictlist):
- for this in my_dictlist:
- if this[key] == value:
- return True
- return False
-
-def get_all_vpce(client):
- vpce_list = client.describe_vpc_endpoints(MaxResults=1000)
- all_vpce_list = []
- while True:
- for item in vpce_list['VpcEndpoints']:
- all_vpce_list.append(item)
- if 'NextToken' in vpce_list:
- vpce_list = client.describe_vpc_endpoints(NextToken=vpce_list['NextToken'], MaxResults=1000)
- else:
- break
- return all_vpce_list
-
-def get_all_api_gateway(client):
- rest_apis_list = client.get_rest_apis(limit=500)
- apis_list = []
- while True:
- for item in rest_apis_list['items']:
- apis_list.append(item)
- if 'position' in rest_apis_list:
- rest_apis_list = client.get_rest_apis(position=rest_apis_list['position'], limit=500)
- else:
- break
- return apis_list
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Parameter value is invalid",
- internalErrorDetails="An ValueError was raised during the validation of the Parameter value",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while(evaluation_copy):
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=resultToken, TestMode=testMode)
- del evaluation_copy[:100]
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_PRIVATE_RESTRICTED/API_GW_PRIVATE_RESTRICTED_test.py b/vendor/github.com/awslabs/aws-config-rules/python/API_GW_PRIVATE_RESTRICTED/API_GW_PRIVATE_RESTRICTED_test.py
deleted file mode 100644
index c492636..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_PRIVATE_RESTRICTED/API_GW_PRIVATE_RESTRICTED_test.py
+++ /dev/null
@@ -1,644 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::ApiGateway::RestApi'
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-apigw_client_mock = MagicMock()
-vpc_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'sts':
- return sts_client_mock
- elif client_name == 'apigateway':
- return apigw_client_mock
- elif client_name == 'ec2':
- return vpc_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('API_GW_PRIVATE_RESTRICTED')
-
-class ComplianceTest(unittest.TestCase):
-
- #VPC descripion api call
- vpc_description = {
- "Vpcs": [
- {
- "VpcId": "vpc-7e934918"
- },
- {
- "VpcId": "vpc-3fd01559"
- }
- ]
- }
-
- vpc_endpoint_description = {
- "VpcEndpoints": [
- {
- "VpcEndpointId": "vpce-00174e7dff8fe43c1",
- "VpcId": "vpc-3fd01559"
- },
- {
- "VpcEndpointId": "vpce-03d70f31b65a06050",
- "VpcId": "vpc-3fd01559"
- }
- ]
- }
-
- apigw_in_regional_mode = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "REGIONAL"
- ]
- },
- "name": "test-api-gateway"
- }
- ]
- }
-
- apigw_edge_optimized_mode = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "EDGE"
- ]
- },
- "name": "test-api-gateway"
- }
- ]
- }
-
- apigw_no_policy_attached = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "name": "test-api-gateway"
- }
- ]
- }
-
- apigw_policy_has_no_allow_statement = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "id": "ck1phpk3ga",
- "name": "test-api-gateway",
- "policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Deny\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":[\\\"execute-api:/*\\\"],\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpce\\\":\\\"vpce-00174e7dff8fe43c1\\\"}}},{\\\"Effect\\\":\\\"Deny\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":[\\\"execute-api:/*\\\"],\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpce\\\":\\\"vpce-00174e7dff8fe43c2\\\"}}}]}"
- }
- ]
- }
-
- apigw_policy_without_proper_condition = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "name": "test-api-gateway",
- "policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":[\\\"execute-api:/*\\\"],\\\"Condition\\\":{\\\"StringNotEquals\\\":{\\\"aws:sourceVpce\\\":\\\"vpce-00174e7dff8fe43c1\\\"}}},{\\\"Effect\\\":\\\"Deny\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":[\\\"execute-api:/*\\\"],\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpce\\\":\\\"vpce-00174e7dff8fe43c2\\\"}}}]}"
- }
- ]
- }
-
- apigw_vpc_not_same_accout = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "name": "test-api-gateway",
- "policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:apigwId\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpc\\\":\\\"vpc-00000000\\\"}}}]}"
- }
- ]
- }
-
- apigw_vpc_endpoint_not_same_account = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "name": "test-api-gateway",
- "policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:apigwId\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpce\\\":\\\"vpce-00000000000000000\\\"}}}]}"
- }
- ]
- }
-
- apigw_vpc_same_and_different_account_combination = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "name": "test-api-gateway",
- "policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:apigwId\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpc\\\":[\\\"vpc-3fd01559\\\",\\\"vpc-00000000\\\"]}}}]}"
- }
- ]
- }
-
- apigw_vpc_endpoint_same_and_different_account_combination = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "name": "test-api-gateway",
- "policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:apigwId\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpce\\\":[\\\"vpce-00000000000000000\\\",\\\"vpce-00174e7dff8fe43c1\\\"]}}}]}"
- }
- ]
- }
-
- apigw_vpc_same_account = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "name": "test-api-gateway",
- "policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:ck1phpk3ga\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpc\\\":\\\"vpc-3fd01559\\\"}}}]}"
- }
- ]
- }
-
- apigw_vpc_endpoint_same_account = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "name": "test-api-gateway",
- "policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:ck1phpk3ga\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpce\\\":\\\"vpce-00174e7dff8fe43c1\\\"}}}]}"
- }
- ]
- }
-
- apigw_multiple_vpc_same_account = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "name": "test-api-gateway",
- "policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:apigwId\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpc\\\":[\\\"vpc-3fd01559\\\",\\\"vpc-7e934918\\\"]}}}]}"
- }
- ]
- }
-
- apigw_multiple_vpc_endpoint_same_account = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "name": "test-api-gateway",
- "policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:apigwId\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpce\\\":[\\\"vpce-03d70f31b65a06050\\\",\\\"vpce-00174e7dff8fe43c1\\\"]}}}]}"
- }
- ]
- }
-
- apigw_vpc_and_vpc_endpoint_same_account = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "name": "test-api-gateway",
- "policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:apigwId\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpc\\\":\\\"vpc-3fd01559\\\",\\\"aws:sourceVpce\\\":\\\"vpce-00174e7dff8fe43c1\\\"}}}]}"
- }
- ]
- }
-
- apigw_vpc_and_vpc_endpoint_different_account = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "id": "apigwId",
- "name": "test-api-gateway",
- "policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:apigwId\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpc\\\":\\\"vpc-00000000\\\",\\\"aws:sourceVpce\\\":\\\"vpce-00000000000000000\\\"}}}]}"
- }
- ]
- }
-
- apigw_vpc_same_and_vpc_endpoint_different_account = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "id": "apigwId",
- "name": "test-api-gateway",
- "policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:apigwId\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpc\\\":\\\"vpc-3fd01559\\\",\\\"aws:sourceVpce\\\":\\\"vpce-00000000000000000\\\"}}}]}"
- }
- ]
- }
-
- apigw_vpc_different_and_vpc_endpoint_same_account = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "id": "apigwId",
- "name": "test-api-gateway",
- "policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:apigwId\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpc\\\":\\\"vpc-00000000\\\",\\\"aws:sourceVpce\\\":\\\"vpce-00174e7dff8fe43c1\\\"}}}]}"
- }
- ]
- }
-
- apigw_multiple_statement_same_account = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "name": "test-api-gateway",
- "policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:apigwId\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpce\\\":\\\"vpce-00174e7dff8fe43c1\\\"}}},{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:apigwId\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpc\\\":\\\"vpc-3fd01559\\\"}}}]}"
- }
- ]
- }
-
- apigw_multiple_statement_different_account = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "name": "test-api-gateway",
- "policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:apigwId\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpce\\\":\\\"vpce-0000000000000000\\\"}}},{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:apigwId\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpc\\\":\\\"vpc-00000000\\\"}}}]}"
- }
- ]
- }
-
- apigw_multiple_statement_one_different_account = {
- "items": [
- {
- "endpointConfiguration": {
- "types": [
- "PRIVATE"
- ]
- },
- "name": "test-api-gateway",
- "policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:apigwId\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpce\\\":\\\"vpce-00174e7dff8fe43c1\\\"}}},{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-west-2:123456789012:apigwId\\/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"aws:sourceVpc\\\":\\\"vpc-00000000\\\"}}}]}"
- }
- ]
- }
-
- vpc_client_mock.describe_vpcs = MagicMock(return_value=vpc_description)
- vpc_client_mock.describe_vpc_endpoints = MagicMock(return_value=vpc_endpoint_description)
-
- #if apigw is regional - not-applicable
- def test_apigw_regional(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_in_regional_mode)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', 'test-api-gateway', 'AWS::ApiGateway::RestApi'))
- assert_successful_evaluation(self, response, resp_expected)
-
- #if apigw is edge optimized - not applicable
- def test_apigw_edge_optimized_mode(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_edge_optimized_mode)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', 'test-api-gateway', 'AWS::ApiGateway::RestApi'))
- assert_successful_evaluation(self, response, resp_expected)
-
- # gateway is private and has no policy attached to it = non-compliant
- def test_apigw_no_policy_attached(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_no_policy_attached)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi', 'No resource policy is attached.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- #private with policy attached and policy has no allow statement = non-compliant
- def test_apigw_no_allow_statement_in_policy(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_policy_has_no_allow_statement)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi', 'This API has no resource policy with an Allow statement.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- #gateway is privte and policy has allow statement and condition does not contain proper strmatch for VPC VPCE - non-compliant
- def test_apigw_policy_with_no_proper_condition(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_policy_without_proper_condition)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi', 'The Allow statement does not have VPC nor a VPCe.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- # gateway is prvate, has allow statement, has stringMatch for VPC/VPCE and VPC does not belong to same account - non compliant
- def test_apigw_vpc_not_same_account(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_vpc_not_same_accout)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi', 'The VPCs are not in the same account than this API Gateway.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- # gateway is prvate, has allow statement, has stringMatch for VPC/VPCE and VPC Endpoint does not belong to same account - non compliant
- def test_apigw_vpc_endpoint_not_same_account(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_vpc_endpoint_not_same_account)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi', 'The VPCEs are not in the same account than this API Gateway.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- # gateway is prvate, has allow statement, has stringMatch for VPC/VPCE and one VPC beongs to same account and another one does not belong to same account - non compliant
- def test_apigw_vpc_same_and_different_account_combination(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_vpc_same_and_different_account_combination)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi', 'The VPCs are not in the same account than this API Gateway.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- # gateway is prvate, has allow statement, has stringMatch for VPC/VPCE and one VPC Endpoint beongs to same account and another one does not belong to same account - non compliant
- def test_apigw_vpc_endpoint_same_and_different_account_combination(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_vpc_endpoint_same_and_different_account_combination)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi', 'The VPCEs are not in the same account than this API Gateway.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- # gateway is private and policy has allows statement with the condition allowing vpc from same vpc
- def test_apigw_vpc_same_account(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_vpc_same_account)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi'))
- assert_successful_evaluation(self, response, resp_expected)
-
- # gateway is private and policy has allows statement with the condition allowing vpc-endpoint from same vpc
- def test_apigw_vpc_endpoint_same_account(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_vpc_endpoint_same_account)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi'))
- assert_successful_evaluation(self, response, resp_expected)
-
- #gateway is private and policy has allow, condition contains string match for muliple vpc from same account
- def test_apigw_multiple_vpc_same_account(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_multiple_vpc_same_account)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi'))
- assert_successful_evaluation(self, response, resp_expected)
-
- #gateway is private and policy has allow, condition contains string match for muliple vpc endpoint from same account
- def test_apigw_multiple_endpoint_same_account(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_multiple_vpc_endpoint_same_account)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_apigw_vpc_vpc_endpoint_same_account(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_vpc_and_vpc_endpoint_same_account)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_apigw_vpc_vpc_endpoint_different_account(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_vpc_and_vpc_endpoint_different_account)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi', 'The VPCs are not in the same account than this API Gateway.'))
- print(response)
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_apigw_vpc_same_vpc_endpoint_different_account(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_vpc_same_and_vpc_endpoint_different_account)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi', 'The VPCEs are not in the same account than this API Gateway.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_apigw_vpc_different_vpc_endpoint_same_account(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_vpc_different_and_vpc_endpoint_same_account)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi', 'The VPCs are not in the same account than this API Gateway.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_apigw_multiple_statement_all_same_account(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_multiple_statement_same_account)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_apigw_multiple_statement_all_different_account(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_multiple_statement_different_account)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi', 'The VPCEs are not in the same account than this API Gateway.'))
- assert_successful_evaluation(self, response, resp_expected)
-
-
- def test_apigw_multiple_statement_one_different_account(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.apigw_multiple_statement_one_different_account)
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'test-api-gateway', 'AWS::ApiGateway::RestApi', 'The VPCs are not in the same account than this API Gateway.'))
- assert_successful_evaluation(self, response, resp_expected)
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertTrue(response['OrderingTimestamp'])
-
- #commenting annotation mathcing, can be uncommented if you need to compare annotations
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- #commenting annotation mathcing, can be uncommented if you need to compare annotations
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(testClass, response, customerErrorCode=None, customerErrorMessage=None):
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- sts_client_mock.reset_mock(return_value=True)
- sts_client_mock.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_PRIVATE_RESTRICTED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/API_GW_PRIVATE_RESTRICTED/parameters.json
deleted file mode 100644
index 4ea7de3..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_PRIVATE_RESTRICTED/parameters.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "Parameters": {
- "InputParameters": "{}",
- "CodeKey": "final-code.zip",
- "SourceRuntime": "python3.6",
- "SourcePeriodic": "One_Hour",
- "RuleName": "API_GW_PRIVATE_RESTRICTED"
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_RESTRICTED_IP/API_GW_RESTRICTED_IP.py b/vendor/github.com/awslabs/aws-config-rules/python/API_GW_RESTRICTED_IP/API_GW_RESTRICTED_IP.py
deleted file mode 100644
index 03e0f05..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_RESTRICTED_IP/API_GW_RESTRICTED_IP.py
+++ /dev/null
@@ -1,528 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-'''
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- api-gw-restricted-to-ip
-
-Description:
- Verify that non-private API GW have a resource based policy which limit their usage based on IP source (configurable)
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::ApiGateway::RestApi
-
-Parameters:
- | --------------------|-----------|-----------------------------------------------|-------------------------|
- | Parameter Name | Type | Description | Notes |
- | ------------------- | --------- | --------------------------------------------- |-------------------------|
- | WhitelistedIPs | Mandatory | IP addresses whitelisted to invoke the rest | Seperated by comma (,) |
- | | | API. | |
- |---------------------|-----------|-----------------------------------------------|-------------------------|
-
-Feature:
- In order to: to limit the access to API
- As: a Security Officer
- I want: To ensure that non-private REST APIs in API GW have a resource based policy which limit their usage based on whitelisted IPs.
-
-Scenarios:
- Scenario 1:
- Given: WhitelistedIPs parameter is not defined
- Then: return Error
-
- Scenario 2:
- Given: WhitelistedIPs parameter has an incorrect value (empty, non-CIDR, list of non-CIDR)
- Then: return Error
-
- Scenario 3:
- Given: API is in private mode
- Then: return NOT_APPLICABLE
-
- Scenario 3:
- Given: WhitelistedIPs parameter is defined and valid
- And: APIs do not have resource policy attached
- Then: return NON_COMPLIANT
-
- Scenario 4:
- Given: WhitelistedIPs parameter is defined and valid
- And: APIs have resource policy attached
- And: The Resource policy does not contain any 'Allow' statement
- Then: return COMPLIANT
-
- Scenario 5:
- Given: WhitelistedIPs parameter is defined and valid
- And: APIs have resource policy attached
- And: The Resource policy does not contain
- Then: return NON_COMPLIANT
-
- With:
- | Options of Policy |
- | any 'Condition' |
- | any 'Condition' about 'IpAddress' |
- | any 'Condition' about 'IpAddress' about 'aws:SourceIp' |
-
- Scenario 6:
- Given: WhitelistedIPs parameter is defined and valid
- And: APIs have resource policy attached
- And: The Resource policy contains a 'Condition' about 'IpAddress' about 'aws:SourceIp'
- And: Those IPs are not a subset of the WhitelistedIPs
- Then: return NON_COMPLIANT
-
- Scenario 7:
- Given: WhitelistedIPs parameter is defined and valid
- And: APIs have resource policy attached
- And: The Resource policy contains a 'Condition' about 'IpAddress' about 'aws:SourceIp'
- And: Those IPs are a subset of the WhitelistedIPs
- Then: return COMPLIANT
-
-'''
-
-import json
-import datetime
-import ipaddress
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::ApiGateway::RestApi'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
-
- Advanced Notes:
- 1 -- the deleted resources are taken care of by the Boilerplate code
- 2 -- if a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
-
- apigw_client = get_client('apigateway', event)
- gateways_list = get_all_api_gateway(apigw_client)
-
- if not gateways_list:
- return None
-
- evaluations = []
- for gateway in gateways_list:
-
- if gateway['endpointConfiguration']['types'] == ['PRIVATE']:
- evaluations.append(build_evaluation(gateway['name'], 'NOT_APPLICABLE', event))
- continue
-
-
- if 'policy' not in gateway:
- evaluations.append(build_evaluation(gateway['name'], 'NON_COMPLIANT', event, annotation='No resource policy is attached.'))
- continue
-
- policy = json.loads(gateway['policy'].replace('\\',''))
-
- if is_policy_allows_more_than_whitelist(policy, rule_parameters):
- evaluations.append(build_evaluation(gateway['name'], 'NON_COMPLIANT', event, annotation='The attached policy allows more than the whitelist.'))
- continue
-
- evaluations.append(build_evaluation(gateway['name'], 'COMPLIANT', event))
-
- return evaluations
-
-def is_policy_allows_more_than_whitelist(policy, whitelist):
- for statement in policy['Statement']:
- if statement['Effect'] != 'Allow':
- continue
-
- if 'Condition' not in statement:
- return True
-
- if 'IpAddress' not in statement['Condition']:
- return True
-
- if 'aws:SourceIp' not in statement['Condition']['IpAddress']:
- return True
-
- if not is_ip_in_whitelist(statement['Condition']['IpAddress']['aws:SourceIp'], whitelist):
- return True
-
- return False
-
-def is_ip_in_whitelist(ip_list_or_str, whitelist):
-
- all_network_in_ip_list = get_all_ip_networks(ip_list_or_str)
- all_network_in_whitelist = get_all_ip_networks(whitelist)
-
- for net in all_network_in_ip_list:
- is_network_included = False
- for net_whitelisted in all_network_in_whitelist:
- try:
- list(net_whitelisted.address_exclude(net))
- is_network_included = True
- except:
- continue
- if not is_network_included:
- return False
- return True
-
-def get_all_ip_networks(ip_list_or_str):
- ip_network_to_return = []
- if isinstance(ip_list_or_str, str):
- ip_network_to_return.append(ipaddress.ip_network(ip_list_or_str, strict=False))
- elif isinstance(ip_list_or_str, list):
- for addr in ip_list_or_str:
- ip_network_to_return.append(ipaddress.ip_network(addr, strict=False))
- else:
- raise ValueError("Unexpected value in the aws:SourceIp field of the policy.")
- return ip_network_to_return
-
-def get_all_api_gateway(client):
- rest_apis_list = client.get_rest_apis(limit=500)
- apis_list = []
- while True:
- for item in rest_apis_list['items']:
- apis_list.append(item)
- if 'position' in rest_apis_list:
- next_position = rest_apis_list['position']
- rest_apis_list = client.get_rest_apis(position=next_position,limit=500)
- else:
- break
- return apis_list
-
-def evaluate_parameters(rule_parameters):
- if 'WhitelistedIPs' not in rule_parameters:
- raise ValueError('The parameter with "WhitelistedIPs" as key must be defined.')
- if not rule_parameters['WhitelistedIPs']:
- raise ValueError('The parameter "WhitelistedIPs" must have a defined value.')
- try:
- cleaned_parameters = rule_parameters['WhitelistedIPs'].replace(', ',',').split(',')
- except:
- raise ValueError('The parameter "WhitelistedIPs" must be a string or a list of strings separated by comma.')
- for addr in cleaned_parameters:
- if not is_ip_address(addr) and not is_ip_network(addr):
- raise ValueError('The value in parameter "WhitelistedIPs" [' + str(addr) + '] is not a valid IP or a valid IP network.')
- return cleaned_parameters
-
-def is_ip_address(addr):
- try:
- ipaddress.ip_address(addr)
- return True
- except:
- return False
-
-def is_ip_network(net):
- try:
- ipaddress.ip_network(net, strict=False)
- return True
- except:
- return False
-
-def build_parameters_value_error_response(ex):
- return build_error_response(internalErrorMessage="Customer error while parsing input parameters",
- internalErrorDetails="Parameter value is invalid",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-####################
-# Helper Functions #
-####################
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function to check if rule parameters exist
-def parameters_exist(parameters):
- return len(parameters) != 0
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evalations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evalations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evalations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
- AWS_CONFIG_CLIENT = get_client('config', event)
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- rule_parameters_clean = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- configuration_item = get_configuration_item(invoking_event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, rule_parameters_clean)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return {'internalErrorMessage': 'Unexpected message type ' + str(invoking_event)}
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while(evaluation_copy):
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=resultToken, TestMode=testMode)
- del evaluation_copy[:100]
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_RESTRICTED_IP/API_GW_RESTRICTED_IP_test.py b/vendor/github.com/awslabs/aws-config-rules/python/API_GW_RESTRICTED_IP/API_GW_RESTRICTED_IP_test.py
deleted file mode 100644
index fbcfba5..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_RESTRICTED_IP/API_GW_RESTRICTED_IP_test.py
+++ /dev/null
@@ -1,264 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-import sys
-import json
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::ApiGateway::RestApi'
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-apigw_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'sts':
- return sts_client_mock
- elif client_name =='apigateway':
- return apigw_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('API_GW_RESTRICTED_IP')
-
-class TestsOnParameter(unittest.TestCase):
-
- def test_user_whitelist_parameters_not_defined(self):
- invalid_param_not_defined = ['{}',
- '{"SomethingElse":"1234578910"}']
- for invalid_entry in invalid_param_not_defined:
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=invalid_entry), {})
- self.assertEqual(response['customerErrorCode'], 'InvalidParameterValueException')
- self.assertEqual(response['customerErrorMessage'], 'The parameter with "WhitelistedIPs" as key must be defined.')
-
- def test_user_whitelist_parameters_no_value(self):
- invalid_param_no_value = '{"WhitelistedIPs": ""}'
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=invalid_param_no_value), {})
- self.assertEqual(response['customerErrorCode'], 'InvalidParameterValueException')
- self.assertEqual(response['customerErrorMessage'], 'The parameter "WhitelistedIPs" must have a defined value.')
-
- def test_user_whitelist_parameters_not_string(self):
- invalid_param_not_string = ['{"WhitelistedIPs": {"test":"test2"}}',
- '{"WhitelistedIPs": 1023456}']
- for invalid_entry in invalid_param_not_string:
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=invalid_entry), {})
- self.assertEqual(response['customerErrorCode'], 'InvalidParameterValueException')
- self.assertEqual(response['customerErrorMessage'], 'The parameter "WhitelistedIPs" must be a string or a list of strings separated by comma.')
-
- def test_user_whitelist_parameters_not_valid(self):
- invalid_param_not_valid = ['{"WhitelistedIPs":"1234578910"}',
- '{"WhitelistedIPs": "10.1.1.1/92"}',
- '{"WhitelistedIPs":"10.1.1.1 10.1.1.2"}',
- '{"WhitelistedIPs":"10.1.1.1/10.1.1.2"}']
- for invalid_entry in invalid_param_not_valid:
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=invalid_entry), {})
- self.assertEqual(response['customerErrorCode'], 'InvalidParameterValueException')
- value = json.loads(invalid_entry)['WhitelistedIPs']
- self.assertEqual(response['customerErrorMessage'], 'The value in parameter "WhitelistedIPs" [' + str(value) + '] is not a valid IP or a valid IP network.')
-
- def test_user_whitelist_parameters_double_comma(self):
- invalid_param_double_comma = '{"WhitelistedIPs":"10.1.1.1,,10.1.1.2"}'
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=invalid_param_double_comma), {})
- self.assertEqual(response['customerErrorCode'], 'InvalidParameterValueException')
- self.assertEqual(response['customerErrorMessage'], 'The value in parameter "WhitelistedIPs" [] is not a valid IP or a valid IP network.')
-
-class TestsOnCompliance(unittest.TestCase):
-
- valid_whitelist_ip_single = '{"WhitelistedIPs":"10.1.1.1"}'
-
- valid_whitelist_ip_network = '{"WhitelistedIPs":"10.1.1.1/24"}'
-
- get_rest_with_api_private = {
- 'items': [{'name': 'name-api-1', 'endpointConfiguration': {'types': ['PRIVATE']}},
- {'name': 'name-api-2', 'endpointConfiguration': {'types': ['PRIVATE']}}]
- }
-
- get_rest_with_apis_no_policy = {
- 'items': [{'name': 'name-api-1', 'endpointConfiguration': {'types': ['EDGE']}},
- {'name': 'name-api-2', 'endpointConfiguration': {'types': ['REGIONAL']}},
- {'name': 'name-api-3', 'endpointConfiguration': {'types': ['PRIVATE']}}]
- }
-
- get_rest_with_apis_policy_no_allow_no_condition_no_ipadress = {
- 'items': [{'name': 'name-api-1-no-allow',
- 'policy': '{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Deny\\\",\\\"Principal\\\":{\\\"AWS\\\":\\\"arn:aws:iam::112233445566:user\\/batman\\\"},\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-east-1:112233445566:4fzg4h5rf2\\\"},{\\\"Effect\\\":\\\"Deny\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-east-1:112233445566:4fzg4h5rf2\\/*\\\",\\\"Condition\\\":{\\\"IpAddress\\\":{\\\"aws:SourceIp\\\":[\\\"10.24.34.0\\/23\\\",\\\"10.24.34.0\\/24\\\"]}}}]}',
- 'endpointConfiguration': {'types': ['EDGE']}},
- {'name': 'name-api-2-no-condition',
- 'policy': '{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":{\\\"AWS\\\":\\\"arn:aws:iam::112233445566:user\\/batman\\\"},\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-east-1:112233445566:4fzg4h5rf2\\\"},{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-east-1:112233445566:4fzg4h5rf2\\\"}]}',
- 'endpointConfiguration': {'types': ['EDGE']}},
- {'name': 'name-api-3-no-ipaddress',
- 'policy': '{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-east-1:112233445566:4fzg4h5rf2\\/*\\\",\\\"Condition\\\":{\\\"NotIpAddress\\\":{\\\"aws:SourceIp\\\":[\\\"10.24.34.0\\/23\\\",\\\"10.24.34.0\\/24\\\"]}}}]}',
- 'endpointConfiguration': {'types': ['EDGE']}}
- ]
- }
-
- def test_no_gw(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value={"items":[]})
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=self.valid_whitelist_ip_single), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_only_private(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.get_rest_with_api_private)
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=self.valid_whitelist_ip_single), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', 'name-api-1'))
- resp_expected.append(build_expected_response('NOT_APPLICABLE', 'name-api-2'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- def test_no_policy(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.get_rest_with_apis_no_policy)
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=self.valid_whitelist_ip_single), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'name-api-1', annotation='No resource policy is attached.'))
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'name-api-2', annotation='No resource policy is attached.'))
- resp_expected.append(build_expected_response('NOT_APPLICABLE', 'name-api-3'))
- assert_successful_evaluation(self, response, resp_expected, 3)
-
- def test_no_allow_no_condition_no_ipadress(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.get_rest_with_apis_policy_no_allow_no_condition_no_ipadress)
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=self.valid_whitelist_ip_single), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'name-api-1-no-allow'))
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'name-api-2-no-condition', annotation='The attached policy allows more than the whitelist.'))
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'name-api-3-no-ipaddress', annotation='The attached policy allows more than the whitelist.'))
- assert_successful_evaluation(self, response, resp_expected, 3)
-
- valid_whitelist_ip = '{"WhitelistedIPs":"10.1.1.1,10.1.2.0/24"}'
-
- get_rest_with_apis_policy_match_whitelist = {
- 'items': [{'name': 'name-api-1-match-whitelist-address',
- 'policy': '{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-east-1:112233445566:4fzg4h5rf2\\/*\\\",\\\"Condition\\\":{\\\"IpAddress\\\":{\\\"aws:SourceIp\\\":[\\\"10.1.1.1\\\"]}}}]}',
- 'endpointConfiguration': {'types': ['EDGE']}},
- {'name': 'name-api-2-match-whitelist-network',
- 'policy': '{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-east-1:112233445566:4fzg4h5rf2\\/*\\\",\\\"Condition\\\":{\\\"IpAddress\\\":{\\\"aws:SourceIp\\\":[\\\"10.1.2.0\\/24\\\"]}}}]}',
- 'endpointConfiguration': {'types': ['EDGE']}}
- ]
- }
- def test_whitelist_condition_matches(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.get_rest_with_apis_policy_match_whitelist)
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=self.valid_whitelist_ip), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'name-api-1-match-whitelist-address'))
- resp_expected.append(build_expected_response('COMPLIANT', 'name-api-2-match-whitelist-network'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- valid_whitelist_ip_more = '{"WhitelistedIPs":"10.1.1.0/24,10.1.2.0/28"}'
-
- get_rest_with_apis_policy_dont_match_whitelist_compliant = {
- 'items': [{'name': 'name-api-1-no-match-address',
- 'policy': '{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-east-1:112233445566:4fzg4h5rf2\\/*\\\",\\\"Condition\\\":{\\\"IpAddress\\\":{\\\"aws:SourceIp\\\":[\\\"10.1.1.10\\\"]}}}]}',
- 'endpointConfiguration': {'types': ['EDGE']}},
- {'name': 'name-api-2-no-match-network',
- 'policy': '{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-east-1:112233445566:4fzg4h5rf2\\/*\\\",\\\"Condition\\\":{\\\"IpAddress\\\":{\\\"aws:SourceIp\\\":[\\\"10.1.2.4\\/30\\\"]}}}]}',
- 'endpointConfiguration': {'types': ['EDGE']}}
- ]
- }
- def test_COMPLIANT_whitelist_condition_dont_match(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.get_rest_with_apis_policy_dont_match_whitelist_compliant)
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=self.valid_whitelist_ip_more), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'name-api-1-no-match-address'))
- resp_expected.append(build_expected_response('COMPLIANT', 'name-api-2-no-match-network'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- get_rest_with_apis_policy_dont_match_whitelist_non_compliant = {
- 'items': [{'name': 'name-api-1-no-match-address',
- 'policy': '{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-east-1:112233445566:4fzg4h5rf2\\/*\\\",\\\"Condition\\\":{\\\"IpAddress\\\":{\\\"aws:SourceIp\\\":\\\"10.1.3.2\\\"}}}]}',
- 'endpointConfiguration': {'types': ['EDGE']}},
- {'name': 'name-api-2-no-match-network',
- 'policy': '{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:us-east-1:112233445566:4fzg4h5rf2\\/*\\\",\\\"Condition\\\":{\\\"IpAddress\\\":{\\\"aws:SourceIp\\\":[\\\"10.1.1.2\\\",\\\"10.1.3.2\\\","10.1.2.0\\/27\\\"]}}}]}',
- 'endpointConfiguration': {'types': ['EDGE']}}
- ]
- }
- def test_NON_COMPLIANT_whitelist_condition_dont_match(self):
- apigw_client_mock.get_rest_apis = MagicMock(return_value=self.get_rest_with_apis_policy_dont_match_whitelist_non_compliant)
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=self.valid_whitelist_ip_more), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'name-api-1-no-match-address', annotation='The attached policy allows more than the whitelist.'))
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'name-api-2-no-match-network', annotation='The attached policy allows more than the whitelist.'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
-
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': True,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': True,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_RESTRICTED_IP/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/API_GW_RESTRICTED_IP/parameters.json
deleted file mode 100644
index 5596259..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/API_GW_RESTRICTED_IP/parameters.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "Parameters": {
- "RuleName": "API_GW_RESTRICTED_IP",
- "SourceRuntime": "python3.6",
- "CodeKey": "API_GW_RESTRICTED_IP.zip",
- "InputParameters": "{\"WhitelistedIPs\":\"10.0.0.0/24\"}",
- "SourcePeriodic": "TwentyFour_Hours"
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/BUSINESS_SUPPORT_OR_ABOVE_ENABLED/BUSINESS_SUPPORT_OR_ABOVE_ENABLED.py b/vendor/github.com/awslabs/aws-config-rules/python/BUSINESS_SUPPORT_OR_ABOVE_ENABLED/BUSINESS_SUPPORT_OR_ABOVE_ENABLED.py
deleted file mode 100644
index 9180275..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/BUSINESS_SUPPORT_OR_ABOVE_ENABLED/BUSINESS_SUPPORT_OR_ABOVE_ENABLED.py
+++ /dev/null
@@ -1,394 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-'''
- #####################################
- ## Gherkin ##
- #####################################
- Rule Name:
- BUSINESS_SUPPORT_OR_ABOVE_ENABLED
-
- Description:
- Check whether the AWS Account is subscribed to the AWS Business Support Plan or above (i.e. Enterprise).
-
- Trigger:
- Periodic
-
- Reports on:
- AWS::::Account
-
- Rule Parameters:
- None
-
- Scenarios:
- Scenario: 1
- Given: AWS Account has ability to use Support API's DescribeCases call with results returned without ClientError Exception
- Then: Return COMPLIANT
- Scenario: 2
- Given: AWS Support API call errors out with SubscriptionRequired exception while using the DescribeCases API call. This error means Business Plan is not currently used for the account
- Then: Return NON_COMPLIANT
- '''
-import json
-import sys
-import datetime
-import boto3
-import botocore
-from botocore.exceptions import ClientError
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- support_client = boto3.client('support')
- account_id = event['accountId']
-
- try:
- support_client.describe_cases()
- return build_evaluation(account_id, 'COMPLIANT', event)
- except ClientError as error:
- if error.response['Error']['Code'] == 'SubscriptionRequiredException':
- annotate = 'This AWS Account is not subscribed to the AWS business Support plan or above.'
- return build_evaluation(account_id, 'NON_COMPLIANT', event, annotation=annotate)
- raise
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None). It will be truncated to 255 if longer.
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = build_annotation(annotation)
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None). It will be truncated to 255 if longer.
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = build_annotation(annotation)
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Build annotation within Service constraints
-def build_annotation(annotation_string):
- if len(annotation_string) > 256:
- return annotation_string[:244] + " [truncated]"
- return annotation_string
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return status in ('OK', 'ResourceDiscovered') and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/BUSINESS_SUPPORT_OR_ABOVE_ENABLED/BUSINESS_SUPPORT_OR_ABOVE_ENABLED_test.py b/vendor/github.com/awslabs/aws-config-rules/python/BUSINESS_SUPPORT_OR_ABOVE_ENABLED/BUSINESS_SUPPORT_OR_ABOVE_ENABLED_test.py
deleted file mode 100644
index 2f989f7..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/BUSINESS_SUPPORT_OR_ABOVE_ENABLED/BUSINESS_SUPPORT_OR_ABOVE_ENABLED_test.py
+++ /dev/null
@@ -1,178 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import botocore
-
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-SUPPORT_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 'support':
- return SUPPORT_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('BUSINESS_SUPPORT_OR_ABOVE_ENABLED')
-
-class ComplianceTest(unittest.TestCase):
- def test_scenario_1(self):
- SUPPORT_CLIENT_MOCK.reset_mock()
- SUPPORT_CLIENT_MOCK.describe_cases = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'SubscriptionRequiredException', 'Message': 'An error occurred (SubscriptionRequiredException) when calling the DescribeCases operation: AWS Premium Support Subscription is required to use this service.'}}, 'operation'))
- resp_expected = []
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected.append(build_expected_response('NON_COMPLIANT', '123456789012', DEFAULT_RESOURCE_TYPE, annotation='This AWS Account is not subscribed to the AWS business Support plan or above.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario_2(self):
- SUPPORT_CLIENT_MOCK.reset_mock()
- payload_response = {'cases':[]}
- SUPPORT_CLIENT_MOCK.describe_cases = MagicMock(response=payload_response)
- resp_expected = []
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected.append(build_expected_response('COMPLIANT', '123456789012', DEFAULT_RESOURCE_TYPE))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- RULE.evaluate_parameters = MagicMock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- RULE.evaluate_parameters = MagicMock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/BUSINESS_SUPPORT_OR_ABOVE_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/BUSINESS_SUPPORT_OR_ABOVE_ENABLED/parameters.json
deleted file mode 100644
index 9fa0e63..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/BUSINESS_SUPPORT_OR_ABOVE_ENABLED/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "CodeKey": "BUSINESS_SUPPORT_OR_ABOVE_ENABLED.zip",
- "SourceRuntime": "python3.6",
- "SourcePeriodic": "TwentyFour_Hours",
- "RuleName": "BUSINESS_SUPPORT_OR_ABOVE_ENABLED",
- "OptionalParameters": "{}",
- "InputParameters": "{}"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDFRONT_LOGGING_ENABLED/CLOUDFRONT_LOGGING_ENABLED.py b/vendor/github.com/awslabs/aws-config-rules/python/CLOUDFRONT_LOGGING_ENABLED/CLOUDFRONT_LOGGING_ENABLED.py
deleted file mode 100644
index aec85e6..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDFRONT_LOGGING_ENABLED/CLOUDFRONT_LOGGING_ENABLED.py
+++ /dev/null
@@ -1,394 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-'''
-
-#####################################
-## Gherkin ##
-#####################################
-Rule Name:
- CLOUDFRONT_LOGGING_ENABLED
-Description:
- Checks whether your CloudFront Distribution has been configured to store logs on an authorized S3 bucket
-Trigger:
- Configuration change on AWS::CloudFront::Distribution
-Resource Type to report on:
- AWS::CloudFront::Distribution
-Rule Parameters:
- | ---------------------- | ---------- | -------------------------------------------------------------- |
- | Parameter Name | Type | Description |
- | ---------------------- | ---------- | -------------------------------------------------------------- |
- | CentralLoggingBucket | Mandatory | Authorized S3 Bucket for storing CloudFront logs |
- | ---------------------- | ---------- | -------------------------------------------------------------- |
-Feature:
- In order to: ensure that the logs for CloudFront distribution are stored and adequately protected for security & compliance purposes
- As: a Security Officer
- I want: CloudFront Distribution has been configured to store logs on an Authorized S3 bucket
-Scenarios:
- Scenario 1:
- Given: the CloudFront Distribution has not been configured to store logs on an Authorized S3 bucket
- then: Return NON_COMPLIANT
- Scenario 2:
- Given: CloudFront Distribution has been configured to store logs
- And: The S3 bucket where logs are stored is not the correctly authorized bucket
- then: Return NON_COMPLIANT
- Scenario 3:
- Given: CloudFront Distribution has been configured to store logs on an authorized S3 bucket
- And: The S3 bucket where logs are stored is the correctly authorized bucket
- then: Return COMPLIANT
-'''
-
-import json
-import datetime
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = ["AWS::CloudFront::Distribution"]
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
- logging_bucket = valid_rule_parameters['CentralLoggingBucket'] + '.s3.amazonaws.com'
-
- if not configuration_item['configuration']['distributionConfig']['logging']['enabled']:
- return build_evaluation_from_config_item(configuration_item, 'NON_COMPLIANT', annotation='Distribution is not configured to store logs.')
-
- if configuration_item['configuration']['distributionConfig']['logging']['bucket'] == logging_bucket:
- return build_evaluation_from_config_item(configuration_item, 'COMPLIANT')
-
- return build_evaluation_from_config_item(configuration_item, 'NON_COMPLIANT', annotation='Distribution is configured to store logs in an unauthorized bucket.')
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- if not 'CentralLoggingBucket' in rule_parameters:
- raise ValueError('Please configure the CentralLoggingBucket parameter.')
- valid_rule_parameters = rule_parameters
-
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Parameter value is invalid",
- internalErrorDetails="An ValueError was raised during the validation of the Parameter value",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
-
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluations, ResultToken=resultToken, TestMode=testMode)
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDFRONT_LOGGING_ENABLED/CLOUDFRONT_LOGGING_ENABLED_test.py b/vendor/github.com/awslabs/aws-config-rules/python/CLOUDFRONT_LOGGING_ENABLED/CLOUDFRONT_LOGGING_ENABLED_test.py
deleted file mode 100644
index 8c43a37..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDFRONT_LOGGING_ENABLED/CLOUDFRONT_LOGGING_ENABLED_test.py
+++ /dev/null
@@ -1,234 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-import sys
-import json
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::CloudFront::Distribution'
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- if client_name == 'sts':
- return sts_client_mock
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('CLOUDFRONT_LOGGING_ENABLED')
-
-class ComplianceTest(unittest.TestCase):
-
- rule_parameters = '{\"CentralLoggingBucket\": \"cloudfront-logs-bucket-here\"}'
-
- def setUp(self):
- pass
-
- cf_distribution_log_disabled = {
- "configuration": {
- "distributionConfig": {
- "logging": {
- "bucket": "",
- "enabled": False
- },
- },
- },
- "ARN":"arn:aws:cloudfront::123456789012:distribution/E1NFJOWF2FZVA6",
- "configurationItemCaptureTime": "2018-11-10T08:22:15.826Z",
- "awsAccountId": "123456789012",
- "configurationItemStatus": "ResourceDiscovered",
- "resourceType": "AWS::CloudFront::Distribution",
- "resourceId": "arn:aws:cloudfront::123456789012:distribution/E1NFJOWF2FZVA6",
- "resourceName": "CFDistribution"
- }
-
- cf_distribution_log_enabled = {
- "configuration": {
- "distributionConfig": {
- "logging": {
- "bucket":"cloudfront-logs-bucket-here" + '.s3.amazonaws.com',
- "enabled": True
- },
- },
- },
- "ARN":"arn:aws:cloudfront::123456789012:distribution/E1NFJOWF2FZVA6",
- "configurationItemCaptureTime": "2018-11-10T08:22:15.826Z",
- "awsAccountId": "123456789012",
- "configurationItemStatus": "ResourceDiscovered",
- "resourceType": "AWS::CloudFront::Distribution",
- "resourceId": "arn:aws:cloudfront::123456789012:distribution/E1NFJOWF2FZVA6",
- "resourceName": "CFDistribution"
- }
-
- cf_distribution_log_enabled_wrong_bucket = {
- "configuration": {
- "distributionConfig": {
- "logging": {
- "bucket":"im-different-bucket" + '.s3.amazonaws.com',
- "enabled": True
- },
- },
- },
- "ARN":"arn:aws:cloudfront::123456789012:distribution/E1NFJOWF2FZVA6",
- "configurationItemCaptureTime": "2018-11-10T08:22:15.826Z",
- "awsAccountId": "123456789012",
- "configurationItemStatus": "ResourceDiscovered",
- "resourceType": "AWS::CloudFront::Distribution",
- "resourceId": "arn:aws:cloudfront::123456789012:distribution/E1NFJOWF2FZVA6",
- "resourceName": "CFDistribution"
- }
-
- def test_cf_distribution_log_enabled(self):
- invoking_event = '{"awsAccountId":"123456789012","messageType":"ConfigurationItemChangeNotification","configurationItem":'+json.dumps(self.cf_distribution_log_enabled)+'}'
- response = rule.lambda_handler(build_lambda_configurationchange_event(invoking_event, self.rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'arn:aws:cloudfront::123456789012:distribution/E1NFJOWF2FZVA6', 'AWS::CloudFront::Distribution'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_cf_distribution_log_disabled(self):
- resp_expected = []
- invoking_event = '{"awsAccountId":"123456789012","messageType":"ConfigurationItemChangeNotification","configurationItem":'+json.dumps(self.cf_distribution_log_disabled)+'}'
- response = rule.lambda_handler(build_lambda_configurationchange_event(invoking_event, self.rule_parameters), {})
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'arn:aws:cloudfront::123456789012:distribution/E1NFJOWF2FZVA6', 'AWS::CloudFront::Distribution', 'Distribution is not configured to store logs.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_cf_distribution_log_enabled_wrong_bucket(self):
- invoking_event = '{"awsAccountId":"123456789012","messageType":"ConfigurationItemChangeNotification","configurationItem":'+json.dumps(self.cf_distribution_log_enabled_wrong_bucket)+'}'
- response = rule.lambda_handler(build_lambda_configurationchange_event(invoking_event, self.rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'arn:aws:cloudfront::123456789012:distribution/E1NFJOWF2FZVA6', 'AWS::CloudFront::Distribution', 'Distribution is configured to store logs in an unauthorized bucket.'))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(testClass, response, customerErrorCode=None, customerErrorMessage=None):
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- sts_client_mock.reset_mock(return_value=True)
- sts_client_mock.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- rule.ASSUME_ROLE_MODE = True
- rule.evaluate_parameters = MagicMock(return_value=True)
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- rule.ASSUME_ROLE_MODE = True
- rule.evaluate_parameters = MagicMock(return_value=True)
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDFRONT_LOGGING_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/CLOUDFRONT_LOGGING_ENABLED/parameters.json
deleted file mode 100644
index bcfced9..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDFRONT_LOGGING_ENABLED/parameters.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "CLOUDFRONT_LOGGING_ENABLED",
- "SourceRuntime": "python3.6",
- "CodeKey": "CLOUDFRONT_LOGGING_ENABLED.zip",
- "InputParameters": "{\"CentralLoggingBucket\": \"cloudfront-logs-bucket-here\"}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::CloudFront::Distribution"
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDFRONT_VIEWER_POLICY_HTTPS/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/CLOUDFRONT_VIEWER_POLICY_HTTPS/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDFRONT_VIEWER_POLICY_HTTPS/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDFRONT_VIEWER_POLICY_HTTPS/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/CLOUDFRONT_VIEWER_POLICY_HTTPS/parameters.json
deleted file mode 100644
index 984e74e..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDFRONT_VIEWER_POLICY_HTTPS/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "CLOUDFRONT_VIEWER_POLICY_HTTPS",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::CloudFront::Distribution",
- "SourceIdentifier": "CLOUDFRONT_VIEWER_POLICY_HTTPS"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDTRAIL_ENABLED_V2/CLOUDTRAIL_ENABLED_V2.py b/vendor/github.com/awslabs/aws-config-rules/python/CLOUDTRAIL_ENABLED_V2/CLOUDTRAIL_ENABLED_V2.py
deleted file mode 100644
index 1ed1b38..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDTRAIL_ENABLED_V2/CLOUDTRAIL_ENABLED_V2.py
+++ /dev/null
@@ -1,567 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-'''
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- cloudtrail-enabled-v2
-
-Description:
- Checks that at least 1 CloudTrail trail is enabled and have all the specified characteristics, if any.
-
-Trigger:
- Periodic
-
-Resource Type to report on:
- AWS::::Account
-
-Rule Parameters:
- | ---------------------- | --------- | -------------------------------------------------------- |
- | Parameter Name | Type | Description |
- | ---------------------- | --------- | -------------------------------------------------------- |
- | S3BucketName | Optional | The S3 bucket name where the trail is logging. |
- | ---------------------- | --------- | -------------------------------------------------------- |
- | EncryptedBoolean | Optional | Boolean to request encryption of the Trail |
- | | | Constraint: True/False |
- | ---------------------- | --------- | -------------------------------------------------------- |
- | KMSKeyArn | Optional | The ARN of the KMS key which encrypt CloudTrail. |
- | | | The EncryptedBoolean must be set to "True". |
- | ---------------------- | --------- | -------------------------------------------------------- |
- | GlobalResourcesBoolean | Optional | Boolean to request Global resources logging. |
- | | | Constraint: True/False |
- | ---------------------- | --------- | -------------------------------------------------------- |
- | MultiRegionBoolean | Optional | Boolean to request Mutli-regions logging. |
- | | | Constraint: True/False |
- | ---------------------- | --------- | -------------------------------------------------------- |
- | ManagementEventBoolean | Optional | Boolean to request the logging of Management Events. |
- | | | Constraint: True/False |
- | ---------------------- | --------- | -------------------------------------------------------- |
- | S3DataEventBoolean | Optional | Boolean to request the logging of all S3 Data Events. |
- | | | Constraint: True/False |
- | ---------------------- | --------- | -------------------------------------------------------- |
- | LambdaEventBoolean | Optional | Boolean to request the logging of all Lambda Events. |
- | | | Constraint: True/False |
- | ---------------------- | --------- | -------------------------------------------------------- |
- | LFIBoolean | Optional | Boolean to request log file integrity enabled. |
- | | | Constraint: True/False |
- | ---------------------- | --------- | -------------------------------------------------------- |
-
-Feature:
- In order to: enforce traceability of APIs
- As: a Security Officer
- I want: To ensure that at least 1 CloudTrail trail logs as I request.
-
-Scenarios:
-
- Scenario 1:
- Given: boolean Parameters not having value: True/False or empty
- Then: Return an error
-
- Scenario 2:
- Given: no CloudTrail trail exist
- Then: return NON_COMPLIANT
-
- Scenario 3:
- Given: no CloudTrail trail is enabled
- Then: return NON_COMPLIANT
-
- Scenario 4:
- Given: at least 1 CloudTrail trail is enabled
- And: none of those delivers succesfully the logs into S3
- Then: return NON_COMPLIANT
-
- Scenario 5:
- Given: S3BucketName is configured and valid
- And: at least 1 CloudTrail trail is enabled
- And: none of those CloudTrail trail logs in S3BucketName
- Then: return NON_COMPLIANT
-
- Scenario 6:
- Given: EncryptedBoolean is configured and valid
- And: at least 1 CloudTrail trail is enabled
- And: none of those CloudTrail trail is encrypted
- Then: return NON_COMPLIANT
-
- Scenario 7:
- Given: EncryptedBoolean is configured and valid
- And: KMSKeyArn is configured and valid
- And: at least 1 CloudTrail trail is enabled
- And: at least 1 of those CloudTrail trail(s) is encrypted
- And: none of those CloudTrail trail(s) is encrpyted with the KMS Key KMSKeyArn
- Then: return NON_COMPLIANT
-
- Scenario 8:
- Given: GlobalResourcesBoolean is configured and valid
- And: at least 1 CloudTrail trail is enabled
- And: none of those CloudTrail trail is logging global resources
- Then: return NON_COMPLIANT
-
- Scenario 9:
- Given: MultiRegionBoolean is configured and valid
- And: at least 1 CloudTrail trail is enabled
- And: none of those CloudTrail trail is logging all the regions
- Then: return NON_COMPLIANT
-
- Scenario 10:
- Given: ManagementEventBoolean is configured and valid
- And: at least 1 CloudTrail trail is enabled
- And: none of those CloudTrail trail is logging management events
- Then: return NON_COMPLIANT
-
- Scenario 11:
- Given: ManagementEventBoolean is configured and valid
- And: at least 1 CloudTrail trail is enabled
- And: at least 1 of those CloudTrail trail is logging management events
- And: none of those CloudTrail trail is logging in ReadWriteType = All
- Then: return NON_COMPLIANT
-
- Scenario 12:
- Given: S3DataEventBoolean is configured and valid
- And: at least 1 CloudTrail trail is enabled
- And: none of those CloudTrail trail is logging S3 data events
- Then: return NON_COMPLIANT
-
- Scenario 13:
- Given: LambdaEventBoolean is configured and valid
- And: at least 1 CloudTrail trail is enabled
- And: none of those CloudTrail trail is logging lambda events
- Then: return NON_COMPLIANT
-
- Scenario 14:
- Given: LFIBoolean is configured and valid
- And: at least 1 CloudTrail trail is enabled
- And: none of those CloudTrail trail has log file integrity
- Then: return NON_COMPLIANT
-
- Scenario 15:
- Given: at least 1 CloudTrail trail is enabled
- And: at least 1 of those CloudTrail trail has all configurations aligned with parameters
- Then: return COMPLIANT
-'''
-
-import json
-import datetime
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
-
- ct_client = get_client('cloudtrail', event)
- trail_list = get_all_trails(ct_client)
- if not trail_list:
- return None
-
- for trail in trail_list:
- print(trail)
- if valid_rule_parameters['GlobalResourcesBoolean'] and not trail['IncludeGlobalServiceEvents']:
- continue
- if valid_rule_parameters['MultiRegionBoolean'] and not trail['IsMultiRegionTrail']:
- continue
- if valid_rule_parameters['LFIBoolean'] and not trail['LogFileValidationEnabled']:
- continue
- if valid_rule_parameters['MultiRegionBoolean'] and not trail['IsMultiRegionTrail']:
- continue
- if valid_rule_parameters['S3BucketName'] and trail['S3BucketName'] != valid_rule_parameters['S3BucketName']:
- continue
- if valid_rule_parameters['EncryptedBoolean'] and 'KmsKeyId' not in trail:
- continue
- if valid_rule_parameters['EncryptedBoolean'] and valid_rule_parameters['KMSKeyArn'] and valid_rule_parameters['KMSKeyArn'] != trail['KmsKeyId']:
- continue
-
- try:
- trail_status = ct_client.get_trail_status(Name=trail['Name'])
- except:
- continue
- if not trail_status['IsLogging']:
- continue
- if 'LatestDeliveryError' in trail_status:
- continue
- if valid_rule_parameters['ManagementEventBoolean'] or valid_rule_parameters['S3DataEventBoolean'] or valid_rule_parameters['LambdaEventBoolean']:
- trail_selector = ct_client.get_event_selectors(TrailName=trail['Name'])['EventSelectors'][0]
- if valid_rule_parameters['ManagementEventBoolean'] and (not trail_selector['IncludeManagementEvents'] or trail_selector['ReadWriteType'] != 'All'):
- continue
- if valid_rule_parameters['S3DataEventBoolean'] and (not trail_selector['DataResources'] or check_data_event(trail_selector['DataResources'], 'AWS::S3::Object', 'arn:aws:s3')):
- continue
- if valid_rule_parameters['LambdaEventBoolean'] and (not trail_selector['DataResources'] or check_data_event(trail_selector['DataResources'], 'AWS::Lambda::Function', 'arn:aws:lambda')):
- continue
- return 'COMPLIANT'
- return 'NON_COMPLIANT'
-
-def check_data_event(list_data_resources, type, value):
- for data_resource in list_data_resources:
- if type == data_resource['Type']:
- for data_resource_value in data_resource['Values']:
- if data_resource_value == value:
- return False
- return True
-
-def get_all_trails(ct_client):
- all_trails = []
- trails_list = ct_client.describe_trails()
- while True:
- all_trails += trails_list['trailList']
- if 'NextToken' in trails_list:
- trails_list = ct_client.describe_trails(NextToken=trails_list['NextToken'])
- else:
- break
- return all_trails
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- valid_rule_parameters = {}
-
- bool_param_list = ['EncryptedBoolean', 'GlobalResourcesBoolean', 'MultiRegionBoolean', 'ManagementEventBoolean', 'S3DataEventBoolean', 'LambdaEventBoolean', 'LFIBoolean']
- for bool_param in bool_param_list:
- if bool_param in rule_parameters:
- if rule_parameters[bool_param] not in ['True', 'False']:
- raise ValueError('The parameter "{}" must be either "True" or "False".'.format(bool_param))
- if rule_parameters[bool_param] == 'True':
- valid_rule_parameters[bool_param] = True
- else:
- valid_rule_parameters[bool_param] = False
- continue
- valid_rule_parameters[bool_param] = False
-
- if 'S3BucketName' not in rule_parameters:
- valid_rule_parameters['S3BucketName'] = ''
- else:
- valid_rule_parameters['S3BucketName'] = rule_parameters['S3BucketName']
-
- if 'KMSKeyArn' not in rule_parameters or not valid_rule_parameters['EncryptedBoolean']:
- valid_rule_parameters['KMSKeyArn'] = ''
- else:
- valid_rule_parameters['KMSKeyArn'] = rule_parameters['KMSKeyArn']
-
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Parameter value is invalid",
- internalErrorDetails="An ValueError was raised during the validation of the Parameter value",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluations, ResultToken=resultToken, TestMode=testMode)
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDTRAIL_ENABLED_V2/CLOUDTRAIL_ENABLED_V2_test.py b/vendor/github.com/awslabs/aws-config-rules/python/CLOUDTRAIL_ENABLED_V2/CLOUDTRAIL_ENABLED_V2_test.py
deleted file mode 100644
index 3909b48..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDTRAIL_ENABLED_V2/CLOUDTRAIL_ENABLED_V2_test.py
+++ /dev/null
@@ -1,378 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-import sys
-import json
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-ct_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'cloudtrail':
- return ct_client_mock
- elif client_name == 'sts':
- return sts_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('CLOUDTRAIL_ENABLED_V2')
-
-class ComplianceTest(unittest.TestCase):
-
- rule_parameters_s3_bucket = '{"S3BucketName":"some-bucket-name"}'
- rule_parameters_encryption = '{"EncryptedBoolean":"True", "KMSKeyArn":"some-key-arn"}'
- rule_parameters_global = '{"GlobalResourcesBoolean":"True"}'
- rule_parameters_multi = '{"MultiRegionBoolean":"True"}'
- rule_parameters_mgmt = '{"ManagementEventBoolean":"True"}'
- rule_parameters_s3_event = '{"S3DataEventBoolean":"True"}'
- rule_parameters_lambda_event = '{"LambdaEventBoolean":"True"}'
- rule_parameters_lfi = '{"LFIBoolean":"True"}'
- rule_parameters_all = '{"S3BucketName":"some-bucket-name", "EncryptedBoolean":"True", "KMSKeyArn":"some-key-arn", "GlobalResourcesBoolean":"True", "MultiRegionBoolean":"True", "ManagementEventBoolean":"True", "S3DataEventBoolean":"True", "LambdaEventBoolean":"True", "LFIBoolean":"True"}'
-
- describe_trail_none = {'trailList': []}
- describe_trail_valid = {
- 'trailList': [{
- 'Name': 'ct-name-1',
- 'S3BucketName': 'other-name',
- 'IncludeGlobalServiceEvents': False,
- 'IsMultiRegionTrail': False,
- 'LogFileValidationEnabled': False,
- 'KmsKeyId': 'some-other-key',
- 'HasCustomEventSelectors': False
- }, {
- 'Name': 'ct-name-2',
- 'S3BucketName': 'cloudtrail-cac-reinvent',
- 'IncludeGlobalServiceEvents': False,
- 'IsMultiRegionTrail': False,
- 'LogFileValidationEnabled': False,
- 'HasCustomEventSelectors': False
- }]}
- describe_trail_valid_no_key = {
- 'trailList': [{
- 'Name': 'ct-name-1',
- }]}
- describe_trail_all = {
- 'trailList': [{
- 'Name': 'ct-name-1',
- 'S3BucketName': 'some-bucket-name',
- 'IncludeGlobalServiceEvents': True,
- 'IsMultiRegionTrail': True,
- 'LogFileValidationEnabled': True,
- 'KmsKeyId': 'some-key-arn',
- 'HasCustomEventSelectors': True
- }]}
-
- get_event_selectors_mgmt_false = {"EventSelectors": [{"IncludeManagementEvents": False}]}
- get_event_selectors_mgmt_notall = {"EventSelectors": [{
- "ReadWriteType": "NotAll",
- "IncludeManagementEvents": True}]}
- get_event_selectors_invalid = {"EventSelectors": [
- {
- "ReadWriteType": "All",
- "IncludeManagementEvents": True,
- "DataResources": [
- {
- "Type": "AWS::S3::Object",
- "Values": ["arn:aws:s3:something"]
- },
- {
- "Type": "AWS::Lambda::Function",
- "Values": ["arn:aws:lambda:something"]
- }
- ]
- }
- ]}
- get_event_selectors_all = {"EventSelectors": [
- {
- "ReadWriteType": "All",
- "IncludeManagementEvents": True,
- "DataResources": [
- {
- "Type": "AWS::S3::Object",
- "Values": ["arn:aws:s3"]
- },
- {
- "Type": "AWS::Lambda::Function",
- "Values": ["arn:aws:lambda"]
- }
- ]
- }
- ]}
-
- logging = {'IsLogging': True}
- no_logging = {'IsLogging': False}
- failed_delivery = {'IsLogging': True, 'LatestDeliveryError': 'some-error'}
-
- def test_scenario01_param_not_valid(self):
- rule.ASSUME_ROLE_MODE = False
- for param in ['EncryptedBoolean', 'GlobalResourcesBoolean', 'MultiRegionBoolean', 'ManagementEventBoolean', 'S3DataEventBoolean', 'LambdaEventBoolean', 'LFIBoolean']:
- invalid_param = {}
- invalid_param[param]='invalid'
- response = rule.lambda_handler(build_lambda_scheduled_event(json.dumps(invalid_param)), {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException', 'The parameter "{}" must be either "True" or "False".'.format(param))
-
- def test_scenario02_no_trail(self):
- rule.ASSUME_ROLE_MODE = False
- ct_client_mock.describe_trails = MagicMock(return_value=self.describe_trail_none)
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario03_not_enabled(self):
- rule.ASSUME_ROLE_MODE = False
- ct_client_mock.describe_trails = MagicMock(return_value=self.describe_trail_valid)
- ct_client_mock.get_trail_status = MagicMock(return_value=self.no_logging)
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario04_no_delivery_success(self):
- rule.ASSUME_ROLE_MODE = False
- ct_client_mock.describe_trails = MagicMock(return_value=self.describe_trail_valid)
- ct_client_mock.get_trail_status = MagicMock(return_value=self.failed_delivery)
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario05_s3_name(self):
- rule.ASSUME_ROLE_MODE = False
- ct_client_mock.describe_trails = MagicMock(return_value=self.describe_trail_valid)
- ct_client_mock.get_trail_status = MagicMock(return_value=self.logging)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.rule_parameters_s3_bucket), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario06_encryption(self):
- rule.ASSUME_ROLE_MODE = False
- ct_client_mock.describe_trails = MagicMock(return_value=self.describe_trail_valid)
- ct_client_mock.get_trail_status = MagicMock(return_value=self.logging)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.rule_parameters_encryption), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario07_encryption_not_same_key(self):
- rule.ASSUME_ROLE_MODE = False
- ct_client_mock.describe_trails = MagicMock(return_value=self.describe_trail_valid_no_key)
- ct_client_mock.get_trail_status = MagicMock(return_value=self.logging)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.rule_parameters_encryption), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario08_global(self):
- rule.ASSUME_ROLE_MODE = False
- ct_client_mock.describe_trails = MagicMock(return_value=self.describe_trail_valid)
- ct_client_mock.get_trail_status = MagicMock(return_value=self.logging)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.rule_parameters_global), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario09_multi(self):
- rule.ASSUME_ROLE_MODE = False
- ct_client_mock.describe_trails = MagicMock(return_value=self.describe_trail_valid)
- ct_client_mock.get_trail_status = MagicMock(return_value=self.logging)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.rule_parameters_multi), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario10_mgmt_false(self):
- rule.ASSUME_ROLE_MODE = False
- ct_client_mock.describe_trails = MagicMock(return_value=self.describe_trail_valid)
- ct_client_mock.get_trail_status = MagicMock(return_value=self.logging)
- ct_client_mock.get_event_selectors = MagicMock(return_value=self.get_event_selectors_mgmt_false)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.rule_parameters_mgmt), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario11_mgmt_noall(self):
- rule.ASSUME_ROLE_MODE = False
- ct_client_mock.describe_trails = MagicMock(return_value=self.describe_trail_valid)
- ct_client_mock.get_trail_status = MagicMock(return_value=self.logging)
- ct_client_mock.get_event_selectors = MagicMock(return_value=self.get_event_selectors_mgmt_notall)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.rule_parameters_mgmt), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario12_s3_event(self):
- rule.ASSUME_ROLE_MODE = False
- ct_client_mock.describe_trails = MagicMock(return_value=self.describe_trail_valid)
- ct_client_mock.get_trail_status = MagicMock(return_value=self.logging)
- ct_client_mock.get_event_selectors = MagicMock(return_value=self.get_event_selectors_invalid)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.rule_parameters_s3_event), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario13_lambda_event(self):
- rule.ASSUME_ROLE_MODE = False
- ct_client_mock.describe_trails = MagicMock(return_value=self.describe_trail_valid)
- ct_client_mock.get_trail_status = MagicMock(return_value=self.logging)
- ct_client_mock.get_event_selectors = MagicMock(return_value=self.get_event_selectors_invalid)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.rule_parameters_lambda_event), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario14_lfi(self):
- rule.ASSUME_ROLE_MODE = False
- ct_client_mock.describe_trails = MagicMock(return_value=self.describe_trail_valid)
- ct_client_mock.get_trail_status = MagicMock(return_value=self.logging)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.rule_parameters_lfi), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario15_all(self):
- rule.ASSUME_ROLE_MODE = False
- ct_client_mock.describe_trails = MagicMock(return_value=self.describe_trail_all)
- ct_client_mock.get_trail_status = MagicMock(return_value=self.logging)
- ct_client_mock.get_event_selectors = MagicMock(return_value=self.get_event_selectors_all)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.rule_parameters_all), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(testClass, response, customerErrorCode=None, customerErrorMessage=None):
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- sts_client_mock.reset_mock(return_value=True)
- sts_client_mock.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDTRAIL_ENABLED_V2/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/CLOUDTRAIL_ENABLED_V2/parameters.json
deleted file mode 100644
index f20a04e..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDTRAIL_ENABLED_V2/parameters.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "CLOUDTRAIL_ENABLED_V2",
- "SourceRuntime": "python3.6",
- "CodeKey": "CLOUDTRAIL_ENABLED_V2.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{\"S3BucketName\":\"\",\"EncryptedBoolean\":\"True\",\"KMSKeyArn\":\"\",\"GlobalResourcesBoolean\":\"True\",\"MultiRegionBoolean\":\"True\",\"ManagementEventBoolean\":\"True\",\"S3DataEventBoolean\":\"True\",\"LambdaEventBoolean\":\"True\",\"LFIBoolean\":\"True\"}",
- "SourcePeriodic": "TwentyFour_Hours",
- "RuleSets": [
- "baseline",
- "rulecriticity:critical",
- "pci",
- "pci:10.1",
- "pci:10.2",
- "pci:10.5",
- "cloudtrail"
- ]
- }
-}
-
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDTRAIL_S3_DATAEVENTS_ENABLED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/CLOUDTRAIL_S3_DATAEVENTS_ENABLED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDTRAIL_S3_DATAEVENTS_ENABLED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDTRAIL_S3_DATAEVENTS_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/CLOUDTRAIL_S3_DATAEVENTS_ENABLED/parameters.json
deleted file mode 100644
index e1c4e2a..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDTRAIL_S3_DATAEVENTS_ENABLED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "CLOUDTRAIL_S3_DATAEVENTS_ENABLED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{\"S3BucketNames\": \"\"}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "CLOUDTRAIL_S3_DATAEVENTS_ENABLED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDWATCH_LOG_GROUP_ENCRYPTED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/CLOUDWATCH_LOG_GROUP_ENCRYPTED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDWATCH_LOG_GROUP_ENCRYPTED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDWATCH_LOG_GROUP_ENCRYPTED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/CLOUDWATCH_LOG_GROUP_ENCRYPTED/parameters.json
deleted file mode 100644
index 1d06833..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CLOUDWATCH_LOG_GROUP_ENCRYPTED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "CLOUDWATCH_LOG_GROUP_ENCRYPTED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{\"KmsKeyId\": \"\"}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "CLOUDWATCH_LOG_GROUP_ENCRYPTED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CLOUD_TRAIL_ENCRYPTION_ENABLED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/CLOUD_TRAIL_ENCRYPTION_ENABLED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CLOUD_TRAIL_ENCRYPTION_ENABLED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CLOUD_TRAIL_ENCRYPTION_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/CLOUD_TRAIL_ENCRYPTION_ENABLED/parameters.json
deleted file mode 100644
index 4ef3d83..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CLOUD_TRAIL_ENCRYPTION_ENABLED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "CLOUD_TRAIL_ENCRYPTION_ENABLED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "CLOUD_TRAIL_ENCRYPTION_ENABLED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CLOUD_TRAIL_LOG_FILE_VALIDATION_ENABLED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/CLOUD_TRAIL_LOG_FILE_VALIDATION_ENABLED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CLOUD_TRAIL_LOG_FILE_VALIDATION_ENABLED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CLOUD_TRAIL_LOG_FILE_VALIDATION_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/CLOUD_TRAIL_LOG_FILE_VALIDATION_ENABLED/parameters.json
deleted file mode 100644
index 42351b9..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CLOUD_TRAIL_LOG_FILE_VALIDATION_ENABLED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "CLOUD_TRAIL_LOG_FILE_VALIDATION_ENABLED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "CLOUD_TRAIL_LOG_FILE_VALIDATION_ENABLED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CMK_BACKING_KEY_ROTATION_ENABLED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/CMK_BACKING_KEY_ROTATION_ENABLED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CMK_BACKING_KEY_ROTATION_ENABLED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/CMK_BACKING_KEY_ROTATION_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/CMK_BACKING_KEY_ROTATION_ENABLED/parameters.json
deleted file mode 100644
index 0133fbf..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/CMK_BACKING_KEY_ROTATION_ENABLED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "CMK_BACKING_KEY_ROTATION_ENABLED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "CMK_BACKING_KEY_ROTATION_ENABLED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/DB_INSTANCE_BACKUP_ENABLED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/DB_INSTANCE_BACKUP_ENABLED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/DB_INSTANCE_BACKUP_ENABLED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/DB_INSTANCE_BACKUP_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/DB_INSTANCE_BACKUP_ENABLED/parameters.json
deleted file mode 100644
index 5e3f2d2..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/DB_INSTANCE_BACKUP_ENABLED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "DB_INSTANCE_BACKUP_ENABLED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{\"backupRetentionPeriod\": \"\", \"preferredBackupWindow\": \"\", \"checkReadReplicas\": \"\"}",
- "SourceEvents": "AWS::RDS::DBInstance",
- "SourceIdentifier": "DB_INSTANCE_BACKUP_ENABLED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/DMS_REPLICATION_NOT_PUBLIC/DMS_REPLICATION_NOT_PUBLIC.py b/vendor/github.com/awslabs/aws-config-rules/python/DMS_REPLICATION_NOT_PUBLIC/DMS_REPLICATION_NOT_PUBLIC.py
deleted file mode 100644
index a739400..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/DMS_REPLICATION_NOT_PUBLIC/DMS_REPLICATION_NOT_PUBLIC.py
+++ /dev/null
@@ -1,397 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-"""
-#####################################
-## Gherkin ##
-#####################################
-Description:
- Checks whether AWS DMS replication instance does have public access.
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::DMS::ReplicationInstance
-
-Scenarios:
- Scenario: 1
- Given: No AWS Replication instance exists
- Then: Return Empty list
- Scenario: 2
- Given: At least one AWS Replication instance exists
- And: PubliclyAccessible is set to True for the AWS Replication instance
- Then: Return NON_COMPLIANT with annotation "This AWS Replication instance has public internet access."
- Scenario: 3
- Given: At least one AWS Replication instance exists
- And: PubliclyAccessible is set to False for the AWS Replication instance
- Then: Return COMPLIANT
-
-"""
-
-import json
-import sys
-import datetime
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::DMS::ReplicationInstance'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- client = get_client('dms', event)
- dms_replication_instances = client.describe_replication_instances()['ReplicationInstances']
- evaluations = []
-
- #SCENARIO 1 No AWS Replication instance exists
- if not dms_replication_instances:
- return None
-
- for dms_instance_details in dms_replication_instances:
- if dms_instance_details['PubliclyAccessible']:
- #SCENARIO 2 PubliclyAccessible is set to True for the AWS Replication instance
- evaluations.append(build_evaluation(dms_instance_details['ReplicationInstanceIdentifier'], 'NON_COMPLIANT', event, annotation='This AWS Replication instance has public internet access.'))
- else:
- #SCENARIO 2 PubliclyAccessible is set to False for the AWS Replication instance
- evaluations.append(build_evaluation(dms_instance_details['ReplicationInstanceIdentifier'], 'COMPLIANT', event))
-
- return evaluations
-
-def evaluate_parameters(rule_parameters):
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event, region=None):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- region -- the region where the client is called (default: None)
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service, region)
- credentials = get_assume_role_credentials(event["executionRoleArn"], region)
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken'],
- region_name=region
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None). It will be truncated to 255 if longer.
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = build_annotation(annotation)
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None). It will be truncated to 255 if longer.
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = build_annotation(annotation)
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Build annotation within Service constraints
-def build_annotation(annotation_string):
- if len(annotation_string) > 256:
- return annotation_string[:244] + " [truncated]"
- return annotation_string
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
-
- return status in ('OK', 'ResourceDiscovered') and not event_left_scope
-
-
-def get_assume_role_credentials(role_arn, region=None):
- sts_client = boto3.client('sts', region)
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/DMS_REPLICATION_NOT_PUBLIC/DMS_REPLICATION_NOT_PUBLIC_test.py b/vendor/github.com/awslabs/aws-config-rules/python/DMS_REPLICATION_NOT_PUBLIC/DMS_REPLICATION_NOT_PUBLIC_test.py
deleted file mode 100644
index b6e5a1e..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/DMS_REPLICATION_NOT_PUBLIC/DMS_REPLICATION_NOT_PUBLIC_test.py
+++ /dev/null
@@ -1,200 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::DMS::ReplicationInstance'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-DMS_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 'dms':
- return DMS_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('DMS_REPLICATION_NOT_PUBLIC')
-
-class ComplianceTest(unittest.TestCase):
-
- describe_dms_instance_sce_2 = {"ReplicationInstances": [{'ReplicationInstanceIdentifier':'instance1', 'PubliclyAccessible': True}, {'ReplicationInstanceIdentifier':'instance2', 'PubliclyAccessible': True}]}
- describe_dms_instance_sce_3 = {"ReplicationInstances": [{'ReplicationInstanceIdentifier':'instance1', 'PubliclyAccessible': False}, {'ReplicationInstanceIdentifier':'instance2', 'PubliclyAccessible': False}]}
- describe_dms_instance_sce_4 = {"ReplicationInstances": [{'ReplicationInstanceIdentifier':'instance1', 'PubliclyAccessible': True}, {'ReplicationInstanceIdentifier':'instance2', 'PubliclyAccessible': False}]}
-
- def test_scenario1_no_instances(self):
- DMS_CLIENT_MOCK.describe_replication_instances = MagicMock(return_value={'ReplicationInstances': []})
- lambda_event = build_lambda_scheduled_event(rule_parameters=None)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario2(self):
- DMS_CLIENT_MOCK.describe_replication_instances = MagicMock(return_value=self.describe_dms_instance_sce_2)
- lambda_event = build_lambda_scheduled_event(rule_parameters=None)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'instance1', annotation='This AWS Replication instance has public internet access.'))
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'instance2', annotation='This AWS Replication instance has public internet access.'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
-
- def test_scenario3(self):
- DMS_CLIENT_MOCK.describe_replication_instances = MagicMock(return_value=self.describe_dms_instance_sce_3)
- lambda_event = build_lambda_scheduled_event(rule_parameters=None)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'instance1'))
- resp_expected.append(build_expected_response('COMPLIANT', 'instance2'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- def test_scenario4(self):
- DMS_CLIENT_MOCK.describe_replication_instances = MagicMock(return_value=self.describe_dms_instance_sce_4)
- lambda_event = build_lambda_scheduled_event(rule_parameters=None)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'instance1', annotation='This AWS Replication instance has public internet access.'))
- resp_expected.append(build_expected_response('COMPLIANT', 'instance2'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- RULE.evaluate_parameters = MagicMock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- RULE.evaluate_parameters = MagicMock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/DMS_REPLICATION_NOT_PUBLIC/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/DMS_REPLICATION_NOT_PUBLIC/parameters.json
deleted file mode 100644
index b8374b9..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/DMS_REPLICATION_NOT_PUBLIC/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "CodeKey": "DMS_REPLICATION_NOT_PUBLIC.zip",
- "SourceRuntime": "python3.6",
- "SourcePeriodic": "One_Hour",
- "RuleName": "DMS_REPLICATION_NOT_PUBLIC",
- "OptionalParameters": "{}",
- "InputParameters": "{}"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/DYNAMODB_ENCRYPTED_CUSTOM/DYNAMODB_ENCRYPTED.py b/vendor/github.com/awslabs/aws-config-rules/python/DYNAMODB_ENCRYPTED_CUSTOM/DYNAMODB_ENCRYPTED.py
deleted file mode 100644
index a360fa1..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/DYNAMODB_ENCRYPTED_CUSTOM/DYNAMODB_ENCRYPTED.py
+++ /dev/null
@@ -1,440 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-'''
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- DYNAMODB_ENCRYPTED
-
-Description:
- Check whether DynamoDB Tables are encrypted.
-
-Trigger:
- Configuration Change on AWS::DynamoDB::Table
-
-Reports on:
- AWS::DynamoDB::Table
-
-Rule Parameters:
- +-------------------+----------+-------------------------------------------------------------+
- | Parameter Name | Type | Description |
- +-------------------+----------+-------------------------------------------------------------+
- | WhitelistedTables | Optional | Enter the resource ID of a Dynamo Table |
- | | | to display COMPLIANT (or list of ID separated by ",") |
- +-------------------+----------+-------------------------------------------------------------+
-
-Feature:
- In order to: enforce strong security
- As: a Database Administrator
- I want: To ensure that all DynamoDB tables are encrypted with the default DynamoDB KMS Key.
-
-Scenarios:
-
- Scenario: 1
- Given: Rules parameter is provided
- And: It contains one or more parameter key other than WhitelistedTables
- Then: Return ERROR
-
- Scenario: 2
- Given: Rules parameter is provided
- And: DynamoDB table is in white-listed Tables
- Then: Return COMPLIANT with annotation
-
- Scenario: 3
- Given: DynamoDB table is not active i.e Deleting/deleted
- Then: Return NOT_APPLICABLE
-
- Scenario: 4
- Given: DynamoDB table is active
- And: DynamoDB table is encrypted
- Then: Return COMPLIANT
-
- Scenario: 5
- Given: DynamoDB table is active
- And: DynamoDB table is not encrypted
- Then: Return NON_COMPLIANT
-
- Scenario: 6
- Given: Rule Lambda function is not allowed to assume Config rule
- Then: Return ERROR
-
- Scenario: 7
- Given: Any unknown error occurred while executing Config Rule
- Then: Return ERROR
-'''
-
-import json
-import datetime
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::DynamoDB::Table'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
- if configuration_item['resourceId'] in valid_rule_parameters:
- return build_evaluation_from_config_item(
- configuration_item,
- 'COMPLIANT',
- annotation="The DynamoDB table \"" + configuration_item['resourceId'] + "\" is whitelisted.")
-
- status_table = configuration_item["configuration"]["tableStatus"]
- if status_table == "DELETING":
- return build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE')
-
- # If ssedescription status is not present or its not 'ENABLED' or 'ENABLING' in
- # configuration_item, meaning the table is not encrypted, return NON_COMPLIANT
- # else return COMPLIANT.
- try:
- status_kms = configuration_item["configuration"]["ssedescription"]["status"]
- if status_kms in ['ENABLED', 'ENABLING']:
- return build_evaluation_from_config_item(configuration_item, 'COMPLIANT')
- except Exception as e:
- pass
-
- return build_evaluation_from_config_item(
- configuration_item,
- 'NON_COMPLIANT',
- annotation="The DynamoDB table \"" + configuration_item['resourceId'] + "\" is not encrypted.")
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- if not rule_parameters:
- return {}
-
- if len(rule_parameters) > 1:
- raise ValueError(str('The parameter (' + str(rule_parameters) + ') has more than one key. The only accepted key is: WhitelistedTables.'))
- elif len(rule_parameters) == 1 and "WhitelistedTables" not in rule_parameters:
- raise ValueError('The parameter (' + str(rule_parameters) + ') has not a valid key.')
-
- whitelist_table_list = rule_parameters['WhitelistedTables'].replace(' ','').split(',')
- return whitelist_table_list
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Customer error while parsing input parameters",
- internalErrorDetails="Parameter value is invalid",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- configuration_item = get_configuration_item(invoking_event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return {'internalErrorMessage': 'Unexpected message type ' + str(invoking_event)}
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while(evaluation_copy):
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=resultToken, TestMode=testMode)
- del evaluation_copy[:100]
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/DYNAMODB_ENCRYPTED_CUSTOM/DYNAMODB_ENCRYPTED_test.py b/vendor/github.com/awslabs/aws-config-rules/python/DYNAMODB_ENCRYPTED_CUSTOM/DYNAMODB_ENCRYPTED_test.py
deleted file mode 100644
index 24900fd..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/DYNAMODB_ENCRYPTED_CUSTOM/DYNAMODB_ENCRYPTED_test.py
+++ /dev/null
@@ -1,266 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-import sys
-import json
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::DynamoDB::Table'
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'sts':
- return sts_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('DYNAMODB_ENCRYPTED')
-
-class TestDynamoDbTablesForCompliance(unittest.TestCase):
-
- def test_Scenario_3_not_applicable_deleted(self):
- rule.ASSUME_ROLE_MODE = False
- invoking_event = build_invoking_event("DELETED", False)
- invoking_event['configurationItem']['configurationItemStatus'] = "ResourceDeleted"
- response = rule.lambda_handler(build_lambda_configurationchange_event(json.dumps(invoking_event)), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', 'config-test-table'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_3_not_applicable_table_deleting(self):
- rule.ASSUME_ROLE_MODE = False
- invoking_event = build_invoking_event("DELETING", False)
- response = rule.lambda_handler(build_lambda_configurationchange_event(json.dumps(invoking_event)), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', 'config-test-table'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_5_noncompliant_table_active_and_non_encrypted(self):
- rule.ASSUME_ROLE_MODE = False
- invoking_event = build_invoking_event("ACTIVE", False)
- response = rule.lambda_handler(build_lambda_configurationchange_event(json.dumps(invoking_event)), {})
- print(response)
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'config-test-table', annotation='The DynamoDB table "config-test-table" is not encrypted.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_4_compliant_table_active_and_encrypted(self):
- rule.ASSUME_ROLE_MODE = False
- invoking_event = build_invoking_event("ACTIVE", True)
- response = rule.lambda_handler(build_lambda_configurationchange_event(json.dumps(invoking_event)), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'config-test-table'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_2_compliant_table_whitelisted_non_encrypted(self):
- rule.ASSUME_ROLE_MODE = False
- invoking_event = build_invoking_event("ACTIVE", False)
- invoking_event['configurationItem']['resourceId'] = 'whitelisted-id'
- rule_parameters = '{"WhitelistedTables":"whitelisted-id"}'
- response = rule.lambda_handler(
- build_lambda_configurationchange_event(
- json.dumps(invoking_event), rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'whitelisted-id', annotation='The DynamoDB table "whitelisted-id" is whitelisted.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_2_compliant_table_whitelisted_encrypted(self):
- rule.ASSUME_ROLE_MODE = False
- invoking_event = build_invoking_event("ACTIVE", True)
- invoking_event['configurationItem']['resourceId'] = 'whitelisted-id'
- rule_parameters = '{"WhitelistedTables":"whitelisted-id"}'
- response = rule.lambda_handler(
- build_lambda_configurationchange_event(
- json.dumps(invoking_event), rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'whitelisted-id', annotation='The DynamoDB table "whitelisted-id" is whitelisted.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_1_invalid_key_parameters(self):
- rule.ASSUME_ROLE_MODE = False
- invoking_event = build_invoking_event("ACTIVE", True)
- rule_parameters = '{"WhitelistedTables":"whitelisted-id", "invalid":"invalid"}'
- response = rule.lambda_handler(
- build_lambda_configurationchange_event(
- json.dumps(invoking_event), rule_parameters), {})
- assert_customer_error_response(
- self,
- response,
- 'InvalidParameterValueException',
- "The parameter ({'WhitelistedTables': 'whitelisted-id', 'invalid': 'invalid'}) has more than one key. The only accepted key is: WhitelistedTables.")
-
- def test_Scenario_1_invalid_parameter(self):
- rule.ASSUME_ROLE_MODE = False
- invoking_event = build_invoking_event("ACTIVE", True)
- rule_parameters = '{"invalid":"invalid"}'
- response = rule.lambda_handler(
- build_lambda_configurationchange_event(
- json.dumps(invoking_event), rule_parameters), {})
- assert_customer_error_response(
- self,
- response,
- 'InvalidParameterValueException',
- "The parameter ({'invalid': 'invalid'}) has not a valid key.")
-
-def build_invoking_event(table_status, sse_enabled):
- configuration_item = build_configuration_item(table_status, sse_enabled)
- return {
- "messageType": "ConfigurationItemChangeNotification",
- "configurationItem": configuration_item
- }
-
-def build_configuration_item(table_status, sse_enabled):
- configuration = {
- "tableStatus": table_status,
- "tableArn": "arn:aws:dynamodb:us-east-1:123456789012:table/config-test-table"}
-
- if sse_enabled:
- configuration["ssedescription"] = {
- "status": "ENABLED"
- }
-
- configuration_item = {
- "awsAccountId": '123456789012',
- "configurationItemCaptureTime": "2018-02-28T11:22:05.874Z",
- "configurationItemStatus": "ResourceDiscovered",
- "arn": "arn:aws:dynamodb:us-east-1:920520484730:table/config-test-table",
- "resourceType": "AWS::DynamoDB::Table",
- "resourceId": "config-test-table",
- "resourceName": "config-test-table",
- "awsRegion": 'us-east-1',
- "configuration": configuration}
- return configuration_item
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- sts_client_mock.reset_mock(return_value=True)
- sts_client_mock.assume_role = MagicMock(return_value=assume_role_response)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(testClass, response, customerErrorCode=None, customerErrorMessage=None):
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
-
-##################
-# Commun Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/DYNAMODB_ENCRYPTED_CUSTOM/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/DYNAMODB_ENCRYPTED_CUSTOM/parameters.json
deleted file mode 100644
index 92b8a94..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/DYNAMODB_ENCRYPTED_CUSTOM/parameters.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "Parameters": {
- "RuleName": "DYNAMODB_ENCRYPTED",
- "SourceRuntime": "python3.6",
- "CodeKey": "DYNAMODB_ENCRYPTED.zip",
- "InputParameters": "{}",
- "SourceEvents": "AWS::DynamoDB::Table"
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/DYNAMODB_TABLE_ENCRYPTION_ENABLED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/DYNAMODB_TABLE_ENCRYPTION_ENABLED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/DYNAMODB_TABLE_ENCRYPTION_ENABLED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/DYNAMODB_TABLE_ENCRYPTION_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/DYNAMODB_TABLE_ENCRYPTION_ENABLED/parameters.json
deleted file mode 100644
index 63ccbff..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/DYNAMODB_TABLE_ENCRYPTION_ENABLED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "DYNAMODB_TABLE_ENCRYPTION_ENABLED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::DynamoDB::Table",
- "SourceIdentifier": "DYNAMODB_TABLE_ENCRYPTION_ENABLED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EBS_ENCRYPTED_VOLUMES_V2/EBS_ENCRYPTED_VOLUMES_V2.py b/vendor/github.com/awslabs/aws-config-rules/python/EBS_ENCRYPTED_VOLUMES_V2/EBS_ENCRYPTED_VOLUMES_V2.py
deleted file mode 100644
index 6271b63..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EBS_ENCRYPTED_VOLUMES_V2/EBS_ENCRYPTED_VOLUMES_V2.py
+++ /dev/null
@@ -1,577 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-'''
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- ebs-encrypted-volumes-v2
-
-Description:
- Check that all EBS volumes are encrypted.
-
-Trigger:
- Configuration Change on AWS::EC2::Volume
-
-Reports on:
- AWS::EC2::Volume
-
-Parameters:
- | ----------------------|-----------|-----------------------------------------------|
- | Parameter Name | Type | Description |
- | ----------------------|-----------|---------------------------------------------- |
- | KmsIdList | Optional | List of ID of the KMS keys that is used to |
- | | | encrypt the volume, separated by comma (,). |
- | ----------------------|-----------|---------------------------------------------- |
- | VolumeExceptionList | Optional | List the EBS volumeId exempted of encryption, |
- | | | separated by comma (,). |
- |-----------------------|-----------|-----------------------------------------------|
- | SubnetExceptionList | Optional | List the subnets exempted of encryption, |
- | | | separated by comma (,). |
- |-----------------------|-----------|-----------------------------------------------|
-
-Feature:
- In order to: to protect the data confidentiality
- As: a Security Officer
- I want: To ensure that all EBS volumes are encrypted.
-
-Scenarios:
- Scenario 1:
- Given: the KmsIdList parameter is not alphanumerical lower case and dashes.
- Then: return an Error
-
- Scenario 2:
- Given: the VolumeExceptionList parameter is not starting with "vol-" and followed by alphanumerical lower case
- Then: return an Error
-
- Scenario 3:
- Given: the SubnetExceptionList parameter is not starting with "subnet-" and followed by alphanumerical lower case
- Then: return an Error
-
- Scenario 4:
- Given: the Volume ID is in the VolumeExceptionList.
- Then: return COMPLIANT with annotation "This volume is part of the exception list."
-
- Scenario 5:
- Given: the Volume ID is not in the VolumeExceptionList.
- And: the Volume is not attached to an EC2 instance.
- And: the Volume is not encrypted.
- Then: return NON_COMPLIANT
-
- Scenario 6:
- Given: the Volume ID is not in the VolumeExceptionList.
- And: the Volume is not attached to an EC2 instance.
- And: the Volume is encrypted.
- And: the KmsIdList parameter is not configured.
- Then: return COMPLIANT
-
- Scenario 7:
- Given: the Volume ID is not in the VolumeExceptionList.
- And: the Volume is not attached to an EC2 instance.
- And: the Volume is encrypted.
- And: the KmsIdList parameter is configured and valid.
- And: the Kms key ID encryting the volume is not in KmsIdList.
- Then: return NON_COMPLIANT
-
- Scenario 8:
- Given: the Volume ID is not in the VolumeExceptionList.
- And: the Volume is not attached to an EC2 instance.
- And: the Volume is encrypted.
- And: the KmsIdList parameter is configured and valid.
- And: the Kms key ID encryting the volume is in KmsIdList.
- Then: return COMPLIANT
-
- Scenario 9:
- Given: the Volume ID is not in the VolumeExceptionList.
- And: the Volume is attached to an EC2 instance.
- And: the EC2 instance's subnet is in SubnetExceptionList.
- Then: return COMPLIANT with annotation "Subnet is part of exception list."
-
- Scenario 10:
- Given: the Volume ID is not in the VolumeExceptionList.
- And: the Volume is attached to an EC2 instance.
- And: the EC2 instance's subnet is not in SubnetExceptionList.
- And: the Volume is not encrypted.
- Then: return NON_COMPLIANT
-
- Scenario 11:
- Given: the Volume ID is not in the VolumeExceptionList.
- And: the Volume is attached to an EC2 instance.
- And: the EC2 instance's subnet is not in SubnetExceptionList.
- And: the Volume is encrypted.
- And: the KmsIdList parameter is not configured.
- Then: return COMPLIANT
-
- Scenario 12:
- Given: the Volume ID is not in the VolumeExceptionList.
- And: the Volume is attached to an EC2 instance.
- And: the EC2 instance's subnet is not in SubnetExceptionList.
- And: the Volume is encrypted.
- And: the KmsIdList parameter is configured and valid.
- And: the Kms key ID encryting the volume is not in KmsIdList.
- Then: return NON_COMPLIANT
-
- Scenario 13:
- Given: the Volume ID is not in the VolumeExceptionList.
- And: the Volume is attached to an EC2 instance.
- And: the EC2 instance's subnet is not in SubnetExceptionList.
- And: the Volume is encrypted.
- And: the KmsIdList parameter is configured and valid.
- And: the Kms key ID encryting the volume is in KmsIdList.
- Then: return COMPLIANT
-'''
-
-import json
-import datetime
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::EC2::Volume'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-# Compliance Evaluation Helper Functions
-def get_subnet_id(instance_id, event):
- subnet_id_list = []
- ec2_client = get_client('ec2', event)
- all_instances = ec2_client.describe_instances(InstanceIds=[instance_id])
- for reservation in all_instances['Reservations']:
- for instance in reservation['Instances']:
- if 'NetworkInterfaces' in instance:
- for network in instance['NetworkInterfaces']:
- subnet_id_list.append(network['SubnetId'])
- return subnet_id_list
-
-def is_in_subnet_exception_list(configuration_item, subnet_exception_list, event):
- if 'attachments' in configuration_item['configuration']:
- for attachment in configuration_item['configuration']['attachments']:
- if 'instanceId' in attachment:
- subnet_id_list = get_subnet_id(attachment['instanceId'], event)
- if subnet_id_list:
- for subnet_id in subnet_id_list:
- if subnet_id in subnet_exception_list:
- return True
- return False
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
-
- if 'VolumeExceptionList' in valid_rule_parameters:
- if configuration_item['configuration']['volumeId'] in valid_rule_parameters['VolumeExceptionList']:
- return build_evaluation_from_config_item(
- configuration_item,
- 'COMPLIANT',
- 'This EBS volume is part of the exception list.')
-
- if 'SubnetExceptionList' in valid_rule_parameters:
- if is_in_subnet_exception_list(configuration_item, valid_rule_parameters['SubnetExceptionList'], event):
- return build_evaluation_from_config_item(
- configuration_item,
- 'COMPLIANT',
- 'This EBS volume is attached to an EC2 instance in a subnet which is part the exception list.')
-
- if configuration_item['configuration']['encrypted']:
- if 'KmsIdList' in valid_rule_parameters:
- if configuration_item['configuration']['kmsKeyId'].split('/')[1] not in valid_rule_parameters['KmsIdList']:
- return build_evaluation_from_config_item(
- configuration_item,
- 'NON_COMPLIANT',
- 'This EBS volume is encrypted, but not with a KMS Key listed in the parameter KmsIdList.')
- return build_evaluation_from_config_item(configuration_item, 'COMPLIANT')
-
- return build_evaluation_from_config_item(configuration_item, 'NON_COMPLIANT')
-
-# Parameter Validation Helper Functions END
-
-# KMS ID Validation
-def validate_kms_id(kms_id):
- if '-' not in kms_id or kms_id[0] == '-' or kms_id[-1] == '-' or '--' in kms_id:
- return False
- kms_id_elements = kms_id.split('-')
- for element in kms_id_elements:
- if (element.isalnum() and element.islower()) or element.isdigit():
- continue
- return False
- return True
-
-def verify_kms_id_list(kms_id_list):
- for kms_id in kms_id_list:
- if not validate_kms_id(kms_id):
- return (False, kms_id)
- return True
-
-# Volume Id Validation
-def verify_volume_exception_list(vol_exception_list):
- for vol_id in vol_exception_list:
- if vol_id.startswith('vol-') and vol_id.split('-')[1].isalnum() and vol_id.islower():
- continue
- return (False, vol_id)
- return True
-
-# Subnet Id Validation
-def verify_subnet_exception_list(subnet_ids):
- for subnet_id in subnet_ids:
- if subnet_id.startswith('subnet-') and subnet_id[7:].isalnum() and subnet_id.islower():
- continue
- return (False, subnet_id)
- return True
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- valid_rule_parameters = {}
-
- if 'KmsIdList' in rule_parameters:
- kms_id_split = rule_parameters['KmsIdList'].split(',')
- kms_id_list = [kms_id.strip() for kms_id in kms_id_split]
- kms_id_list_check = verify_kms_id_list(kms_id_list)
- if isinstance(kms_id_list_check, tuple) and not kms_id_list_check[0]:
- raise ValueError('Invalid KMS ID specified: {}'.format(kms_id_list_check[1]))
- valid_rule_parameters['KmsIdList'] = kms_id_list
-
- if 'VolumeExceptionList' in rule_parameters:
- vol_exception_split = rule_parameters['VolumeExceptionList'].split(',')
- vol_exception_list = [vol.strip() for vol in vol_exception_split]
- vol_exception_list_check = verify_volume_exception_list(vol_exception_list)
- if isinstance(vol_exception_list_check, tuple) and not vol_exception_list_check[0]:
- raise ValueError('Invalid Volume ID specified: {}'.format(vol_exception_list_check[1]))
- valid_rule_parameters['VolumeExceptionList'] = vol_exception_list
-
- if 'SubnetExceptionList' in rule_parameters:
- sub_exception_split = rule_parameters['SubnetExceptionList'].split(',')
- sub_exception_list = [sub.strip() for sub in sub_exception_split]
- sub_exception_list_check = verify_subnet_exception_list(sub_exception_list)
- if isinstance(sub_exception_list_check, tuple) and not sub_exception_list_check[0]:
- raise ValueError('Invalid Subnet ID specified: {}'.format(sub_exception_list_check[1]))
- valid_rule_parameters['SubnetExceptionList'] = sub_exception_list
-
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Parameter value is invalid",
- internalErrorDetails="An ValueError was raised during the validation of the Parameter value",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(
- configurationItemSummary['resourceType'],
- configurationItemSummary['resourceId'],
- configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- # if status == 'ResourceDeleted':
- # print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- # print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response(
- "Customer error while making API request",
- str(ex),
- ex.response['Error']['Code'],
- ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- # print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- # print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluations, ResultToken=resultToken, TestMode=testMode)
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- # print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EBS_ENCRYPTED_VOLUMES_V2/EBS_ENCRYPTED_VOLUMES_V2_test.py b/vendor/github.com/awslabs/aws-config-rules/python/EBS_ENCRYPTED_VOLUMES_V2/EBS_ENCRYPTED_VOLUMES_V2_test.py
deleted file mode 100644
index a72d004..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EBS_ENCRYPTED_VOLUMES_V2/EBS_ENCRYPTED_VOLUMES_V2_test.py
+++ /dev/null
@@ -1,412 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-import sys
-import json
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::EC2::Volume'
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-ec2_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'sts':
- return sts_client_mock
- elif client_name == 'ec2':
- return ec2_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('EBS_ENCRYPTED_VOLUMES_V2')
-
-
-def getRuleParameters(validity, paramName=None):
- validParameters = {
- "VolumeExceptionList": "vol-01",
- "SubnetExceptionList": "subnet-01",
- "KmsIdList": "415ee9cc-9beb-4217-bec8-45cabmfrbee6f"
- }
- invalidVolumeParams = [
- "vol-050607259f67717d5, asdef",
- "1234",
- "vol23r4ts",
- "vol-948567,vol- 246934, vol-235646 vol-35446"
- ]
- invalidKmsKeyIdParams = [
- "-9beb-4217-bec8-45cab7ase6f",
- "415ee9cc-9beb-4217-bec8-45cab7abee6f,415ee9cc9beb4217bec845cab7abee6f",
- "415ee9cc-9beb-4217-bec8-",
- "415ee9cc-9beb-4217-bec8-asff--sdvrvbrv"
- ]
- invalidSubnetParams = [
- 'subnetd2cd14ba',
- 'd2cd14ba',
- 'subnet-d2cd14ba subnet-d2cd1443',
- 'd2cd14ba-subnet'
- ]
- if not validity:
- if paramName == 'VolumeExceptionList':
- return invalidVolumeParams
- if paramName == 'SubnetExceptionList':
- return invalidSubnetParams
- if paramName == 'KmsIdList':
- return invalidKmsKeyIdParams
- return validParameters
-
-def constructConfiguration(encrypted, volumeId, kmsKeyId=None, attachments=''):
- return {
- "encrypted":encrypted,
- "kmsKeyId":kmsKeyId,
- "volumeId":volumeId,
- "attachments":attachments
- }
-
-def constructConfigItem(configuration, volumeId):
- configItem = {
- 'relatedEvents': [],
- 'relationships': [],
- 'configuration': configuration,
- 'configurationItemVersion': "1.3",
- 'configurationItemCaptureTime': "2018-07-02T03:37:52.418Z",
- 'supplementaryConfiguration': {},
- 'configurationStateId': 1532049940079,
- 'awsAccountId': "SAMPLE",
- 'configurationItemStatus': "ResourceDiscovered",
- 'resourceType': "AWS::EC2::Volume",
- 'resourceId': volumeId,
- 'resourceName': None,
- 'ARN': "arn:aws:ec2:ap-south-1:822333706:volume/{}".format(volumeId),
- 'awsRegion': "ap-south-1",
- 'configurationStateMd5Hash': "",
- 'resourceCreationTime': "2018-07-19T06:27:28.289Z",
- 'tags': {}
- }
- return configItem
-
-def constructInvokingEvent(configItem):
- invokingEvent = {
- "configurationItemDiff": None,
- "configurationItem": configItem,
- "notificationCreationTime": "SAMPLE",
- "messageType": "ConfigurationItemChangeNotification",
- "recordVersion": "SAMPLE"
- }
- return invokingEvent
-
-class InvalidParametersTest(unittest.TestCase):
-
- def test_Scenario_1_invalid_kmsKeyParameters(self):
- params = {"KmsIdList": "-1,s30c-du4-3erdft-"}
- configuration = constructConfiguration(encrypted=True, kmsKeyId='sdf434-dsvfb3-4545-dfvfdv', volumeId="vol-w4t4434")
- invoking_event = constructInvokingEvent(constructConfigItem(configuration, "volumeId"))
- lambdaEvent = build_lambda_configurationchange_event(invoking_event=invoking_event, rule_parameters=params)
- response = rule.lambda_handler(lambdaEvent, {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
- def test_Scenario_2_invalid_volumeParameters(self):
- params = {"VolumeExceptionList": "oool-0003,vol--0sd4e"}
- configuration = constructConfiguration(encrypted=True, kmsKeyId='sdf434-dsvfb3-4545-dfvfdv', volumeId="vol-w4t4434")
- invoking_event = constructInvokingEvent(constructConfigItem(configuration, "volumeId"))
- lambdaEvent = build_lambda_configurationchange_event(invoking_event=invoking_event, rule_parameters=params)
- response = rule.lambda_handler(lambdaEvent, {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
- def test_Scenario_3_invalid_subnetParameters(self):
- params = {"SubnetExceptionList": "aaasssubnet-02,subnet03edfy45,dhu47dh-subnet"}
- configuration = constructConfiguration(encrypted=True, kmsKeyId='sdf434-dsvfb3-4545-dfvfdv', volumeId="vol-w4t4434")
- invoking_event = constructInvokingEvent(constructConfigItem(configuration, "volumeId"))
- lambdaEvent = build_lambda_configurationchange_event(invoking_event=invoking_event, rule_parameters=params)
- response = rule.lambda_handler(lambdaEvent, {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
-class ComplianceTest(unittest.TestCase):
-
- def test_Scenario_4_volumeinVolumeExceptionList(self):
- rule_parameters = getRuleParameters(True, '')
- configuration = constructConfiguration(encrypted=False, volumeId="vol-01")
- invoking_event = constructInvokingEvent(constructConfigItem(configuration, "vol-01"))
- event = build_lambda_configurationchange_event(invoking_event, rule_parameters)
- response = rule.lambda_handler(event, {})
- resp_expected = []
- resp_expected.append(build_expected_response(
- 'COMPLIANT',
- 'vol-01',
- annotation='This EBS volume is part of the exception list.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_6_volumeencrypted_noKMSparam(self):
- rule_parameters = {"VolumeExceptionList": "vol-0003", "SubnetExceptionList": "subnet-01"}
- configuration = constructConfiguration(encrypted=True, kmsKeyId='sdf434-dsvfb3-4545-dfvfdv', volumeId="vol-01")
- invoking_event = constructInvokingEvent(constructConfigItem(configuration, "vol-01"))
- event = build_lambda_configurationchange_event(invoking_event, rule_parameters)
- response = rule.lambda_handler(event, {})
- resp_expected = []
- resp_expected.append(build_expected_response(
- 'COMPLIANT',
- 'vol-01'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_5_volumeNOTencrypted_noKMSparam(self):
- rule_parameters = {"VolumeExceptionList": "vol-0003", "SubnetExceptionList": "subnet-01"}
- configuration = constructConfiguration(encrypted=False, volumeId="vol-01")
- invoking_event = constructInvokingEvent(constructConfigItem(configuration, "vol-01"))
- event = build_lambda_configurationchange_event(invoking_event, rule_parameters)
- response = rule.lambda_handler(event, {})
- resp_expected = []
- resp_expected.append(build_expected_response(
- 'NON_COMPLIANT',
- 'vol-01'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_7_volumeencrypted_KMSKeyInvalid(self):
- rule_parameters = getRuleParameters(True, '')
- configuration = constructConfiguration(
- encrypted=True,
- kmsKeyId='arn:aws:kms:region-all-1:123456798877:key/sdf434-dsvfb3-4545-dfvfdv',
- volumeId="vol-02")
- invoking_event = constructInvokingEvent(constructConfigItem(configuration, "vol-02"))
- event = build_lambda_configurationchange_event(invoking_event, rule_parameters)
- response = rule.lambda_handler(event, {})
- resp_expected = []
- resp_expected.append(build_expected_response(
- 'NON_COMPLIANT',
- 'vol-02',
- annotation='This EBS volume is encrypted, but not with a KMS Key listed in the parameter KmsIdList.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_8_volumeencrypted_KMSKeyValid(self):
- rule_parameters = getRuleParameters(True, '')
- configuration = constructConfiguration(
- encrypted=True,
- kmsKeyId='arn:aws:kms:region-all-1:123456798877:key/415ee9cc-9beb-4217-bec8-45cabmfrbee6f',
- volumeId="vol-02")
- invoking_event = constructInvokingEvent(constructConfigItem(configuration, "vol-02"))
- event = build_lambda_configurationchange_event(invoking_event, rule_parameters)
- response = rule.lambda_handler(event, {})
- resp_expected = []
- resp_expected.append(build_expected_response(
- 'COMPLIANT',
- 'vol-02'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_9_volumeSubnetinSubnetExceptionList(self):
- ec2_mock.describe_instances = MagicMock(return_value={"Reservations":[{"Instances":[{"NetworkInterfaces":[{"SubnetId":"subnet-02"}]}]}]})
- rule_parameters = {
- "VolumeExceptionList": "vol-0003",
- "SubnetExceptionList": "subnet-02",
- "KmsIdList": "115ff9cc-9beb-4517-bec8-45cabmfrbee6f"
- }
- configuration = constructConfiguration(encrypted=False, volumeId="vol-01", attachments=[{"instanceId":"i-02"}])
- invoking_event = constructInvokingEvent(constructConfigItem(configuration, "vol-01"))
- event = build_lambda_configurationchange_event(invoking_event, rule_parameters)
- response = rule.lambda_handler(event, {})
- resp_expected = []
- resp_expected.append(build_expected_response(
- 'COMPLIANT',
- 'vol-01',
- annotation='This EBS volume is attached to an EC2 instance in a subnet which is part the exception list.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_10_volumeNotEncrSubnetNotinSubnetList(self):
- ec2_mock.describe_instances = MagicMock(return_value={"Reservations":[{"Instances":[{"SubnetId":"subnet-02"}]}]})
- rule_parameters = getRuleParameters(True, '')
- configuration = constructConfiguration(encrypted=False, volumeId="vol-02", attachments=[{"instanceId":"i-02"}])
- invoking_event = constructInvokingEvent(constructConfigItem(configuration, "vol-02"))
- event = build_lambda_configurationchange_event(invoking_event, rule_parameters)
- response = rule.lambda_handler(event, {})
- resp_expected = []
- resp_expected.append(build_expected_response(
- 'NON_COMPLIANT',
- 'vol-02'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_11_volumeEncryptedNoKMSNoSubnetExceptionNoVolumeException(self):
- ec2_mock.describe_instances = MagicMock(return_value={"Reservations":[{"Instances":[{"SubnetId":"subnet-02"}]}]})
- rule_parameters = {"VolumeExceptionList": "vol-0003", "SubnetExceptionList": "subnet-01"}
- configuration = constructConfiguration(
- encrypted=True,
- kmsKeyId='arn:aws:kms:region-all-1:123456798877:key/415ee9cc-9beb-4217-bec8-45cabmfrbee6f',
- volumeId="vol-02",
- attachments=[{"instanceId":"i-02"}])
- invoking_event = constructInvokingEvent(constructConfigItem(configuration, "vol-02"))
- event = build_lambda_configurationchange_event(invoking_event, rule_parameters)
- response = rule.lambda_handler(event, {})
- resp_expected = []
- resp_expected.append(build_expected_response(
- 'COMPLIANT',
- 'vol-02'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_12_volumeEncryptedNotWithProperKMSNoSubnetExceptionNoVolumeException(self):
- ec2_mock.describe_instances = MagicMock(return_value={"Reservations":[{"Instances":[{"SubnetId":"subnet-02"}]}]})
- rule_parameters = {
- "VolumeExceptionList": "vol-0003",
- "SubnetExceptionList": "subnet-01",
- "KmsIdList": "115ff9cc-9beb-4517-bec8-45cabmfrbee6f"
- }
- configuration = constructConfiguration(
- encrypted=True,
- kmsKeyId='arn:aws:kms:region-all-1:123456798877:key/415ee9cc-9beb-4217-bec8-45cabmfrbee6f',
- volumeId="vol-02",
- attachments=[{"instanceId":"i-02"}])
- invoking_event = constructInvokingEvent(constructConfigItem(configuration, "vol-02"))
- event = build_lambda_configurationchange_event(invoking_event, rule_parameters)
- response = rule.lambda_handler(event, {})
- resp_expected = []
- resp_expected.append(build_expected_response(
- 'NON_COMPLIANT',
- 'vol-02',
- annotation='This EBS volume is encrypted, but not with a KMS Key listed in the parameter KmsIdList.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_13_volumeEncryptedWithProperKMSNoSubnetExceptionNoVolumeException(self): #Scenario13
- ec2_mock.describe_instances = MagicMock(return_value={"Reservations":[{"Instances":[{"SubnetId":"subnet-02"}]}]})
- rule_parameters = getRuleParameters(True, '')
- configuration = constructConfiguration(
- encrypted=True,
- kmsKeyId='arn:aws:kms:region-all-1:123456798877:key/415ee9cc-9beb-4217-bec8-45cabmfrbee6f',
- volumeId="vol-02",
- attachments=[{"instanceId":"i-02"}]
- )
- invoking_event = constructInvokingEvent(constructConfigItem(configuration, "vol-02asd"))
- event = build_lambda_configurationchange_event(invoking_event, rule_parameters)
- response = rule.lambda_handler(event, {})
- resp_expected = []
- resp_expected.append(build_expected_response(
- 'COMPLIANT',
- 'vol-02asd'))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': json.dumps(invoking_event),
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = json.dumps(rule_parameters)
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(testClass, response, customerErrorCode=None, customerErrorMessage=None):
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- sts_client_mock.reset_mock(return_value=True)
- sts_client_mock.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EBS_ENCRYPTED_VOLUMES_V2/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/EBS_ENCRYPTED_VOLUMES_V2/parameters.json
deleted file mode 100644
index b819f0e..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EBS_ENCRYPTED_VOLUMES_V2/parameters.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "Parameters": {
- "RuleName": "EBS_ENCRYPTED_VOLUMES_V2",
- "SourceRuntime": "python3.6",
- "CodeKey": "EBS_ENCRYPTED_VOLUMES_V2.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{\"VolumeExceptionList\": \"\", \"SubnetExceptionList\": \"\"}",
- "SourceEvents": "AWS::EC2::Volume",
- "RuleSets": [
- "rulecriticity:high",
- "pci",
- "pci:3.4"
- ]
- }
-}
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EBS_SNAPSHOT_PUBLIC_RESTORABLE_CHECK/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/EBS_SNAPSHOT_PUBLIC_RESTORABLE_CHECK/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EBS_SNAPSHOT_PUBLIC_RESTORABLE_CHECK/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EBS_SNAPSHOT_PUBLIC_RESTORABLE_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/EBS_SNAPSHOT_PUBLIC_RESTORABLE_CHECK/parameters.json
deleted file mode 100644
index 2b42ff4..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EBS_SNAPSHOT_PUBLIC_RESTORABLE_CHECK/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "EBS_SNAPSHOT_PUBLIC_RESTORABLE_CHECK",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "EBS_SNAPSHOT_PUBLIC_RESTORABLE_CHECK"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_ASSOCIATION_COMPLIANCE_STATUS_CHECK/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/EC2_ASSOCIATION_COMPLIANCE_STATUS_CHECK/Readme.md
deleted file mode 100644
index 3311431..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_ASSOCIATION_COMPLIANCE_STATUS_CHECK/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder assists you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_ASSOCIATION_COMPLIANCE_STATUS_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/EC2_ASSOCIATION_COMPLIANCE_STATUS_CHECK/parameters.json
deleted file mode 100644
index 10fcb4d..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_ASSOCIATION_COMPLIANCE_STATUS_CHECK/parameters.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "EC2_MANAGEDINSTANCE_ASSOCIATION_COMPLIANCE_STATUS_CHECK",
- "SourceRuntime": null,
- "CodeKey": null,
- "SourceEvents": "AWS::SSM::AssociationCompliance",
- "OptionalParameters": "{}",
- "InputParameters": "{}",
- "SourceIdentifier": "EC2_MANAGEDINSTANCE_ASSOCIATION_COMPLIANCE_STATUS_CHECK",
- "RuleSets": [
- "baseline",
- "rulecriticity:high",
- "pci",
- "pci:6.2",
- "ssm"
- ]
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_MANAGED_BY_SSM/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_MANAGED_BY_SSM/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_MANAGED_BY_SSM/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_MANAGED_BY_SSM/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_MANAGED_BY_SSM/parameters.json
deleted file mode 100644
index e9e2309..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_MANAGED_BY_SSM/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "EC2_INSTANCE_MANAGED_BY_SSM",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::EC2::Instance,AWS::SSM::ManagedInstanceInventory",
- "SourceIdentifier": "EC2_INSTANCE_MANAGED_BY_SSM"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_NO_PUBLIC_IP/EC2_INSTANCE_NO_PUBLIC_IP.py b/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_NO_PUBLIC_IP/EC2_INSTANCE_NO_PUBLIC_IP.py
deleted file mode 100644
index 77fe69a..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_NO_PUBLIC_IP/EC2_INSTANCE_NO_PUBLIC_IP.py
+++ /dev/null
@@ -1,365 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-"""
-#####################################
-## Gherkin ##
-#####################################
-Rule Name:
- EC2_INSTANCE_NO_PUBLIC_IP
-Description:
- Checks whether Amazon EC2 instances have a public IP association or not. The rule is NON_COMPLIANT if the publicIp field is present in the Amazon EC2 instance configuration item. This rule applies only to IPv4.
-Trigger:
- Configuration Change on AWS::EC2::Instance
-Reports on:
- AWS::EC2::Instance
-Rule Parameters:
- None
-Scenarios:
- Scenario: 1
- Given: The publicIp field is present in the Amazon EC2 instance configuration item.
- Then: Return NON_COMPLIANT
- Scenario: 2
- Given: The publicIp field is not present in the Amazon EC2 instance configuration item.
- Then: Return COMPLIANT
-"""
-
-import re
-import json
-import sys
-import datetime
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::EC2::Instance'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- if not re.findall('\\bpublicIp\\b', str(configuration_item['configuration']['networkInterfaces'])):
- return build_evaluation_from_config_item(configuration_item, 'COMPLIANT')
- return build_evaluation_from_config_item(configuration_item, 'NON_COMPLIANT', annotation='This Amazon EC2 Instance uses a public IP.')
-
-def evaluate_parameters(rule_parameters):
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return status in ("OK", "ResourceDiscovered") and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_NO_PUBLIC_IP/EC2_INSTANCE_NO_PUBLIC_IP_test.py b/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_NO_PUBLIC_IP/EC2_INSTANCE_NO_PUBLIC_IP_test.py
deleted file mode 100644
index 49b82b3..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_NO_PUBLIC_IP/EC2_INSTANCE_NO_PUBLIC_IP_test.py
+++ /dev/null
@@ -1,285 +0,0 @@
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::EC2::Instance'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('EC2_INSTANCE_NO_PUBLIC_IP')
-
-class ComplianceTest(unittest.TestCase):
-
- #Scenario 1 test cases: NON_COMPLIANT
- def test_scenario_1a_pri_eni_pri_ip_public(self):
- invoking_event_non_compliant = '{"configurationItem":{"configuration":{"networkInterfaces": [\
- {\
- "privateIpAddresses": [\
- {\
- "association": {\
- "ipOwnerId": "amazon",\
- "publicDnsName": "ec2-public-ip.region.compute.amazonaws.com",\
- "publicIp": "public-ip"\
- },\
- "primary": "True",\
- "privateDnsName": "ip-private-ip.region.compute.internal",\
- "privateIpAddress": "private-ip"\
- }\
- ]\
- }]},"configurationItemCaptureTime":"2018-07-02T03:37:52.418Z","awsAccountId":"123456789012","configurationItemStatus":"ResourceDiscovered","resourceType":"AWS::EC2::Instance","resourceId":"some-resource-id"},"messageType":"ConfigurationItemChangeNotification"}'
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event_non_compliant), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'some-resource-id', 'AWS::EC2::Instance', 'This Amazon EC2 Instance uses a public IP.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario_1b_pri_eni_sec_ip_public(self):
- invoking_event_non_compliant = '{"configurationItem":{"configuration":{"networkInterfaces": [\
- {\
- "privateIpAddresses": [\
- {\
- "association": "None",\
- "primary": "True",\
- "privateDnsName": "ip-private-ip.region.compute.internal",\
- "privateIpAddress": "private-ip"\
- },\
- {\
- "association": {\
- "ipOwnerId": "account-id",\
- "publicDnsName": "ec2-public-ip.region.compute.amazonaws.com",\
- "publicIp": "public-ip"\
- },\
- "primary": "False",\
- "privateDnsName": "ip-private-ip.region.compute.internal",\
- "privateIpAddress": "private-ip"\
- }\
- ]\
- }]},"configurationItemCaptureTime":"2018-07-02T03:37:52.418Z","awsAccountId":"123456789012","configurationItemStatus":"ResourceDiscovered","resourceType":"AWS::EC2::Instance","resourceId":"some-resource-id"},"messageType":"ConfigurationItemChangeNotification"}'
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event_non_compliant), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'some-resource-id', 'AWS::EC2::Instance', 'This Amazon EC2 Instance uses a public IP.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario_1c_sec_eni_pri_ip_public(self):
- invoking_event_non_compliant = '{"configurationItem":{"configuration": {"networkInterfaces": [\
- {\
- "privateIpAddresses": [\
- {\
- "association": {\
- "ipOwnerId": "account-id",\
- "publicDnsName": "ec2-public-ip.region.compute.amazonaws.com",\
- "publicIp": "public-ip"\
- },\
- "primary": "True",\
- "privateDnsName": "ip-private-ip.region.compute.internal",\
- "privateIpAddress": "private-ip"\
- }\
- ]\
- },\
- {\
- "privateIpAddresses": [\
- {\
- "association": "None",\
- "primary": "True",\
- "privateDnsName": "ip-private-ip.region.compute.internal",\
- "privateIpAddress": "private-ip"\
- }\
- ]\
- }]},"configurationItemCaptureTime":"2018-07-02T03:37:52.418Z","awsAccountId":"123456789012","configurationItemStatus":"ResourceDiscovered","resourceType":"AWS::EC2::Instance","resourceId":"some-resource-id"},"messageType":"ConfigurationItemChangeNotification"}'
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event_non_compliant), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'some-resource-id', 'AWS::EC2::Instance', 'This Amazon EC2 Instance uses a public IP.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario_1d_sec_eni_sec_ip_public(self):
- invoking_event_non_compliant = '{"configurationItem":{"configuration": { "networkInterfaces": [\
- {\
- "privateIpAddresses": [\
- {\
- "association": "None",\
- "primary": "True",\
- "privateDnsName": "ip-private-ip.region.compute.internal",\
- "privateIpAddress": "private-ip"\
- },\
- {\
- "association": {\
- "ipOwnerId": "account-id",\
- "publicDnsName": "ec2-public-ip.region.compute.amazonaws.com",\
- "publicIp": "public-ip"\
- },\
- "primary": "False",\
- "privateDnsName": "ip-private-ip.region.compute.internal",\
- "privateIpAddress": "private-ip"\
- }\
- ]\
- },\
- { \
- "privateIpAddresses": [\
- {\
- "association": "None",\
- "primary": "True",\
- "privateDnsName": "ip-private-ip.region.compute.internal",\
- "privateIpAddress": "private-ip"\
- },\
- {\
- "association": "None",\
- "primary": "False",\
- "privateDnsName": "ip-private-ip.region.compute.internal",\
- "privateIpAddress": "private-ip"\
- }\
- ]\
- }]},"configurationItemCaptureTime":"2018-07-02T03:37:52.418Z","awsAccountId":"123456789012","configurationItemStatus":"ResourceDiscovered","resourceType":"AWS::EC2::Instance","resourceId":"some-resource-id"},"messageType":"ConfigurationItemChangeNotification"}'
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event_non_compliant), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'some-resource-id', 'AWS::EC2::Instance', 'This Amazon EC2 Instance uses a public IP.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- #Scenario 2 test case: COMPLIANT
- def test_scenario_2_no_public_ip(self):
- invoking_event_compliant = '{"configurationItem":{"configuration":{"networkInterfaces": [\
- {\
- "privateIpAddresses": [\
- {\
- "association": "None",\
- "primary": "True",\
- "privateDnsName": "ip-private-ip.region.compute.internal",\
- "privateIpAddress": "private-ip"\
- }\
- ]\
- }]},"configurationItemCaptureTime":"2018-07-02T03:37:52.418Z","awsAccountId":"123456789012","configurationItemStatus":"ResourceDiscovered","resourceType":"AWS::EC2::Instance","resourceId":"some-resource-id"},"messageType":"ConfigurationItemChangeNotification"}'
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event_compliant), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'some-resource-id', 'AWS::EC2::Instance'))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_NO_PUBLIC_IP/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_NO_PUBLIC_IP/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_NO_PUBLIC_IP/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_NO_PUBLIC_IP/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_NO_PUBLIC_IP/parameters.json
deleted file mode 100644
index 7a13853..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_INSTANCE_NO_PUBLIC_IP/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "EC2_INSTANCE_NO_PUBLIC_IP",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::EC2::Instance",
- "SourceIdentifier": "EC2_INSTANCE_NO_PUBLIC_IP"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_PATCH_COMPLIANCE_STATUS_CHECK/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/EC2_PATCH_COMPLIANCE_STATUS_CHECK/Readme.md
deleted file mode 100644
index 3311431..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_PATCH_COMPLIANCE_STATUS_CHECK/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder assists you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_PATCH_COMPLIANCE_STATUS_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/EC2_PATCH_COMPLIANCE_STATUS_CHECK/parameters.json
deleted file mode 100644
index d421ab8..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_PATCH_COMPLIANCE_STATUS_CHECK/parameters.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "EC2_MANAGEDINSTANCE_PATCH_COMPLIANCE_STATUS_CHECK",
- "SourceRuntime": null,
- "CodeKey": null,
- "SourceEvents": "AWS::SSM::PatchCompliance",
- "OptionalParameters": "{}",
- "InputParameters": "{}",
- "SourceIdentifier": "EC2_MANAGEDINSTANCE_PATCH_COMPLIANCE_STATUS_CHECK",
- "RuleSets": [
- "baseline",
- "rulecriticity:high",
- "pci",
- "pci:6.2",
- "ssm"
- ]
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_SECURITY_GROUP_ATTACHED_TO_ENI/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/EC2_SECURITY_GROUP_ATTACHED_TO_ENI/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_SECURITY_GROUP_ATTACHED_TO_ENI/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_SECURITY_GROUP_ATTACHED_TO_ENI/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/EC2_SECURITY_GROUP_ATTACHED_TO_ENI/parameters.json
deleted file mode 100644
index 9983072..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_SECURITY_GROUP_ATTACHED_TO_ENI/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "EC2_SECURITY_GROUP_ATTACHED_TO_ENI",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::EC2::SecurityGroup",
- "SourceIdentifier": "EC2_SECURITY_GROUP_ATTACHED_TO_ENI"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_SECURITY_GROUP_NOT_USED/EC2_SECURITY_GROUP_NOT_USED.py b/vendor/github.com/awslabs/aws-config-rules/python/EC2_SECURITY_GROUP_NOT_USED/EC2_SECURITY_GROUP_NOT_USED.py
deleted file mode 100644
index f5c5dfc..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_SECURITY_GROUP_NOT_USED/EC2_SECURITY_GROUP_NOT_USED.py
+++ /dev/null
@@ -1,366 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-'''
-
-Description:
- Checks that Security Groups are attached to EC2 instances or elastic network interfaces (ENIs). The rule returns NON_COMPLIANT for a Security Group if it's not associated with any Elastic Network Interface.
-Trigger:
- Configuration Changes
-Reports on:
- AWS::EC2::SecurityGroup
-Rule Parameters:
- None
-Scenarios:
- Scenario: 1
- Given: 'relationships' in the configuration item does not contain a Network interface Id in resourceId
- Then: Return NON_COMPLIANT with annnotation 'This Security Group is not associated with any resource'.
- Scenario: 2
- Given: 'relationships' in the configuration item contains at least one Network interface Id in resourceId
- Then: Return COMPLIANT
-'''
-
-import json
-import sys
-import datetime
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::EC2::SecurityGroup'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- for relation in configuration_item["relationships"]:
- #resourceId for eni: 'eni-123456abcdefghi12'
- if relation['resourceId'][0:3] == "eni":
- return build_evaluation_from_config_item(configuration_item, "COMPLIANT")
- return build_evaluation_from_config_item(configuration_item, "NON_COMPLIANT", "This Amazon EC2 Security Group is not associated with any Amazon Elastic Network Interface.")
-
-def evaluate_parameters(rule_parameters):
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return status in ("OK", "ResourceDiscovered") and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- print("event", event)
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_SECURITY_GROUP_NOT_USED/EC2_SECURITY_GROUP_NOT_USED_test.py b/vendor/github.com/awslabs/aws-config-rules/python/EC2_SECURITY_GROUP_NOT_USED/EC2_SECURITY_GROUP_NOT_USED_test.py
deleted file mode 100644
index ea154d0..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_SECURITY_GROUP_NOT_USED/EC2_SECURITY_GROUP_NOT_USED_test.py
+++ /dev/null
@@ -1,160 +0,0 @@
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::EC2::SecurityGroup'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('EC2_SECURITY_GROUP_NOT_USED')
-
-class ComplianceTest(unittest.TestCase):
-
- #Gherkin Scenario 1: Security group is not associated with any ENI/EC2
- def test_security_group_not_associated(self):
- invoking_event = '{"configurationItem":{"configuration":{"groupName":"security-group-1"}, "relationships":[{"resourceId":"vpc-8b04sge2","resourceName":null,"resourceType":"AWS::EC2::VPC","name":"Is contained in Vpc"}], "configurationItemStatus":"OK", "resourceType":"AWS::EC2::SecurityGroup", "configurationItemCaptureTime":"2019-04-28T07:49:40.797Z", "resourceId":"sg-011af0a7f107d6sg6"}, "messageType":"ConfigurationItemChangeNotification"}'
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event), '{}')
- resp_expected = []
- resp_expected.append(build_expected_response("NON_COMPLIANT", "sg-011af0a7f107d6sg6", annotation="This Amazon EC2 Security Group is not associated with any Amazon Elastic Network Interface."))
- assert_successful_evaluation(self, response, resp_expected)
-
- #Gherkin Scenario 2: Security group is associated with atleast 1 ENI/EC2
- def test_security_group_associated(self):
- invoking_event = '{"configurationItem":{"configuration":{"groupName":"security-group-2"}, "relationships":[{"resourceId":"vpc-8b04sge2","resourceName":null,"resourceType":"AWS::EC2::VPC","name":"Is contained in Vpc"},{"resourceId":"eni-123456abcdefghi18","resourceName":null,"resourceType":"AWS::EC2::NetworkInterface","name":"Is associated with NetworkInterface"}], "configurationItemStatus":"OK", "resourceType":"AWS::EC2::SecurityGroup", "configurationItemCaptureTime":"2019-04-28T07:49:40.797Z", "resourceId":"sg-011af0a7f107d6sg7"}, "messageType":"ConfigurationItemChangeNotification"}'
- response = RULE.lambda_handler(build_lambda_configurationchange_event(invoking_event), '{}')
- resp_expected = []
- resp_expected.append(build_expected_response("COMPLIANT", "sg-011af0a7f107d6sg7"))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_SECURITY_GROUP_NOT_USED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/EC2_SECURITY_GROUP_NOT_USED/parameters.json
deleted file mode 100644
index 839e33e..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_SECURITY_GROUP_NOT_USED/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "CodeKey": "EC2_SECURITY_GROUP_NOT_USED.zip",
- "SourceRuntime": "python3.6",
- "RuleName": "EC2_SECURITY_GROUP_NOT_USED",
- "SourceEvents": "AWS::EC2::SecurityGroup",
- "OptionalParameters": "{}",
- "InputParameters": "{}"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME/EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME.py b/vendor/github.com/awslabs/aws-config-rules/python/EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME/EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME.py
deleted file mode 100644
index fcdc804..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME/EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME.py
+++ /dev/null
@@ -1,451 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-'''
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- ec2-tag-matches-instance-profile-nane
-
-Description:
- Checks that the proper role is attached to the EC2 instances.
-
-Trigger:
- Configuration Changes on AWS::EC2::Instance
-
-Resource Type to report on:
- AWS::EC2::Instance
-
-Rule Parameters:
- None
-
-Feature:
- In order to: ensure the proper permissions
- As: a Security Officer
- I want: To ensure that Instance Profile name matches a particular EC2 tag.
-
-Scenarios:
-
- Scenario 1:
- Given: An EC2 instance has no tag with the key equals to TAG_KEY.
- Then: Return NOT_APPLICABLE
-
- Scenario 2:
- Given: An EC2 instance has a tag with the tag key equals to TAG_KEY.
- And: The tag value does not include TAG_VALUE_MUST_INCLUDE.
- And: This EC2 instance has no instance profile.
- Then: Return NOT_APPLICABLE
-
- Scenario 3:
- Given: An EC2 instance has a tag with the tag key equals to TAG_KEY.
- And: The tag value does not include TAG_VALUE_MUST_INCLUDE.
- And: This EC2 instance has an instance profile.
- And: This instance profile name does not include NAME_ROLE_MUST_INCLUDE
- Then: Return NOT_APPLICABLE
-
- Scenario 4:
- Given: An EC2 instance has a tag with the tag key equals to TAG_KEY.
- And: The tag value does not include TAG_VALUE_MUST_INCLUDE.
- And: This EC2 instance has an instance profile.
- And: This instance profile name includes NAME_ROLE_MUST_INCLUDE
- Then: Return NON_COMPLIANT
-
- Scenario 5:
- Given: An EC2 instance has a tag with the tag key equals to TAG_KEY.
- And: The tag value includes TAG_VALUE_MUST_INCLUDE.
- And: This EC2 instance has no instance profile.
- Then: Return NON_COMPLIANT
-
- Scenario 6:
- Given: An EC2 instance has a tag with the tag key equals to TAG_KEY.
- And: The tag value includes TAG_VALUE_MUST_INCLUDE.
- And: This EC2 instance has an instance profile.
- And: This instance profile name does not include NAME_ROLE_MUST_INCLUDE
- Then: Return NON_COMPLIANT
-
- Scenario 7:
- Given: An EC2 instance has a tag with the tag key equals to TAG_KEY.
- And: The tag value includes TAG_VALUE_MUST_INCLUDE.
- And: This EC2 instance has an instance profile.
- And: This instance profile name includes NAME_ROLE_MUST_INCLUDE
- Then: Return COMPLIANT
-
-'''
-
-import json
-import datetime
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-TAG_KEY = 'application_role'
-TAG_VALUE_MUST_INCLUDE = 'DB'
-NAME_ROLE_MUST_INCLUDE = '.db.'
-DEFAULT_RESOURCE_TYPE = 'AWS::EC2::Instance'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
- # Scenario 1 : EC2 instance has no tag with key equal to TAG_KEY.
- if not TAG_KEY in configuration_item["tags"]:
- return 'NOT_APPLICABLE'
-
- tag_value = configuration_item['tags'][TAG_KEY].upper()
- is_tag_value_include = False
- if tag_value.find(TAG_VALUE_MUST_INCLUDE) >= 0:
- is_tag_value_include = True
-
- # Scenario 2 : EC2 instance does not have TAG_VALUE_MUST_INCLUDE in tag value and has no instance profile.
- if not is_tag_value_include and not configuration_item["configuration"]['iamInstanceProfile']:
- return 'NOT_APPLICABLE'
-
- # Scenario 5 : EC2 instance has TAG_VALUE_MUST_INCLUDE in tag value but does not have an IAM instance profile
- if is_tag_value_include and not configuration_item["configuration"]['iamInstanceProfile']:
- return build_evaluation_from_config_item( configuration_item, 'NON_COMPLIANT', "Tag value for '" + TAG_KEY + "' has '" + TAG_VALUE_MUST_INCLUDE + "' but there is no IAM instance profile for the resource")
-
- name_role = configuration_item["configuration"]['iamInstanceProfile']['arn'].split(':')[-1].split('/')[-1].lower()
- is_name_role_include = False
- if name_role.find(NAME_ROLE_MUST_INCLUDE) >= 0:
- is_name_role_include = True
-
- # Scenario 3 : EC2 instance does not have TAG_VALUE_MUST_INCLUDE in tag value and instance profile does not have NAME_ROLE_MUST_INCLUDE in IAM instance profile
- if not is_tag_value_include and not is_name_role_include:
- return 'NOT_APPLICABLE'
-
- # Scenario 4 : EC2 instance does not have TAG_VALUE_MUST_INCLUDE in tag value but has NAME_ROLE_MUST_INCLUDE in IAM instance profile
- if not is_tag_value_include and is_name_role_include:
- return build_evaluation_from_config_item( configuration_item, 'NON_COMPLIANT', "Tag value for '" + TAG_KEY + "' doesn't have '" + TAG_VALUE_MUST_INCLUDE + "' but IAM Instance Profile has '" + NAME_ROLE_MUST_INCLUDE + "'")
-
- # Scenario 6 : EC2 instance has TAG_VALUE_MUST_INCLUDE in tag value but does not have NAME_ROLE_MUST_INCLUDE in IAM instance profile
- if is_tag_value_include and not is_name_role_include:
- return build_evaluation_from_config_item( configuration_item, 'NON_COMPLIANT', "Tag value for '" + TAG_KEY + "' has '" + TAG_VALUE_MUST_INCLUDE + "' but IAM Instance Profile doesn't have '" + NAME_ROLE_MUST_INCLUDE + "'")
-
- # Scenario 7 : EC2 instance has TAG_VALUE_MUST_INCLUDE in tag value and NAME_ROLE_MUST_INCLUDE in IAM instance profile
- return 'COMPLIANT'
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Customer error while parsing input parameters",
- internalErrorDetails="Parameter value is invalid",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
- AWS_CONFIG_CLIENT = get_client('config', event)
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- configuration_item = get_configuration_item(invoking_event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return {'internalErrorMessage': 'Unexpected message type ' + str(invoking_event)}
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluations, ResultToken=resultToken, TestMode=testMode)
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME/EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME_test.py b/vendor/github.com/awslabs/aws-config-rules/python/EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME/EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME_test.py
deleted file mode 100644
index b70fe46..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME/EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME_test.py
+++ /dev/null
@@ -1,206 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-import json
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::EC2::Instance'
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-ec2_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'sts':
- return sts_client_mock
- elif client_name == 'ec2':
- return ec2_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME')
-
-class SampleTest(unittest.TestCase):
-
- # Scenario 1 : EC2 instance has no tag with key equal to TAG_KEY.
- def test_rule_scenario1(self):
- invoking_event = build_invoking_event("",None)
- response = rule.lambda_handler(build_lambda_event(ruleParameters='{}', invoking_event=invoking_event), "")
- expected_response = []
- expected_response.append(build_expected_response("NOT_APPLICABLE", "some-resource-id"))
- assert_successful_evaluation(self, response, expected_response)
-
- # Scenario 2 : EC2 instance does not have TAG_VALUE_MUST_INCLUDE in tag value and has no instance profile.
- def test_rule_scenario2(self):
- application_role = {"application_role": "blah"}
- invoking_event = build_invoking_event(application_role,None)
- response = rule.lambda_handler(build_lambda_event(ruleParameters='{}', invoking_event=invoking_event), "")
- expected_response = []
- expected_response.append(build_expected_response("NOT_APPLICABLE", "some-resource-id"))
- assert_successful_evaluation(self, response, expected_response)
-
- # Scenario 3 : EC2 instance does not have TAG_VALUE_MUST_INCLUDE in tag value and instance profile does not have NAME_ROLE_MUST_INCLUDE in IAM instance profile
- def test_rule_scenario3(self):
- application_role = {"application_role": "blah"}
- instance_profile = {"arn": "arn:aws:iam::123456789012:instance-profile/aws-poc.np.non-db.ec2role.iaminstancerole"}
- invoking_event = build_invoking_event(application_role, instance_profile)
- response = rule.lambda_handler(build_lambda_event(ruleParameters='{}', invoking_event=invoking_event), "")
- expected_response = []
- expected_response.append(build_expected_response("NOT_APPLICABLE", "some-resource-id"))
- assert_successful_evaluation(self, response, expected_response)
-
- # Scenario 4 : EC2 instance does not have TAG_VALUE_MUST_INCLUDE in tag value but has NAME_ROLE_MUST_INCLUDE in IAM instance profile
- def test_rule_scenario4(self):
- application_role = {"application_role": "blah"}
- instance_profile = {"arn": "arn:aws:iam::123456789012:instance-profile/aws-poc.np.db.ec2role.iaminstancerole"}
- invoking_event = build_invoking_event(application_role, instance_profile)
- response = rule.lambda_handler(build_lambda_event(ruleParameters='{}', invoking_event=invoking_event), "")
- expected_response = []
- expected_response.append(build_expected_response("NON_COMPLIANT", "some-resource-id", annotation="Tag value for 'application_role' doesn't have 'DB' but IAM Instance Profile has '.db.'"))
- assert_successful_evaluation(self, response, expected_response)
-
- # Scenario 5 : EC2 instance has TAG_VALUE_MUST_INCLUDE in tag value but does not have an IAM instance profile
- def test_rule_scenario5(self):
- application_role = {"application_role": "DB/APP"}
- instance_profile = None
- invoking_event = build_invoking_event(application_role, instance_profile)
- response = rule.lambda_handler(build_lambda_event(ruleParameters='{}', invoking_event=invoking_event), "")
- expected_response = []
- expected_response.append(build_expected_response("NON_COMPLIANT", "some-resource-id", annotation="Tag value for 'application_role' has 'DB' but there is no IAM instance profile for the resource"))
- assert_successful_evaluation(self, response, expected_response)
-
- # Scenario 6 : EC2 instance has TAG_VALUE_MUST_INCLUDE in tag value but does not have NAME_ROLE_MUST_INCLUDE in IAM instance profile
- def test_rule_scenario6(self):
- application_role = {"application_role": "DB/APP"}
- instance_profile = {"arn": "arn:aws:iam::123456789012:instance-profile/aws-poc.np.non-db.ec2role.iaminstancerole"}
- invoking_event = build_invoking_event(application_role, instance_profile)
- response = rule.lambda_handler(build_lambda_event(ruleParameters='{}', invoking_event=invoking_event), "")
- expected_response = []
- expected_response.append(build_expected_response("NON_COMPLIANT", "some-resource-id", annotation="Tag value for 'application_role' has 'DB' but IAM Instance Profile doesn't have '.db.'"))
- assert_successful_evaluation(self, response, expected_response)
-
- # Scenario 7 : EC2 instance has TAG_VALUE_MUST_INCLUDE in tag value and NAME_ROLE_MUST_INCLUDE in IAM instance profile
- def test_rule_scenario7(self):
- application_role = {"application_role": "DB/APP"}
- instance_profile = {"arn": "arn:aws:iam::123456789012:instance-profile/aws-poc.np.db.ec2role.iaminstancerole"}
- invoking_event = build_invoking_event(application_role, instance_profile)
- response = rule.lambda_handler(build_lambda_event(ruleParameters='{}', invoking_event=invoking_event), "")
- expected_response = []
- expected_response.append(build_expected_response("COMPLIANT", "some-resource-id"))
- assert_successful_evaluation(self, response, expected_response)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_event(ruleParameters, invoking_event):
- return {
- 'executionRoleArn': 'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'ruleParameters': ruleParameters,
- 'accountId': 'account-id',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken': 'token',
-
- }
-
-def build_invoking_event(tags, iamInstanceProfileARN):
- invoking_event = {
- "messageType":"ConfigurationItemChangeNotification",
- "configurationItem":{
- "resourceType":"AWS::EC2::Instance",
- "resourceId": "some-resource-id",
- "configurationItemStatus": "OK",
- "configurationItemCaptureTime": "anytime",
- "tags": tags,
- "configuration": { "iamInstanceProfile": iamInstanceProfileARN }
- }
- }
- return json.dumps(invoking_event)
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': True,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': True,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME/parameters.json
deleted file mode 100644
index cda1fe5..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME/parameters.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "Parameters": {
- "RuleName": "EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME",
- "SourceRuntime": "python3.6",
- "CodeKey": "EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME.zip",
- "InputParameters": "{}",
- "SourceEvents": "AWS::EC2::Instance"
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_VOLUME_INUSE_CHECK/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/EC2_VOLUME_INUSE_CHECK/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_VOLUME_INUSE_CHECK/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EC2_VOLUME_INUSE_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/EC2_VOLUME_INUSE_CHECK/parameters.json
deleted file mode 100644
index d1427c6..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EC2_VOLUME_INUSE_CHECK/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "EC2_VOLUME_INUSE_CHECK",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{\"deleteOnTermination\": \"\"}",
- "SourceEvents": "AWS::EC2::Volume",
- "SourceIdentifier": "EC2_VOLUME_INUSE_CHECK"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EFS_ENCRYPTED_CHECK/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/EFS_ENCRYPTED_CHECK/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EFS_ENCRYPTED_CHECK/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EFS_ENCRYPTED_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/EFS_ENCRYPTED_CHECK/parameters.json
deleted file mode 100644
index 8f58bf5..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EFS_ENCRYPTED_CHECK/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "EFS_ENCRYPTED_CHECK",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{\"KmsKeyId\": \"\"}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "EFS_ENCRYPTED_CHECK"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK/ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK.py b/vendor/github.com/awslabs/aws-config-rules/python/ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK/ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK.py
deleted file mode 100644
index 8061a98..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK/ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK.py
+++ /dev/null
@@ -1,436 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-'''
-Rule Name:
- ELASTICACHE_REDIS_CLUSTER_AUTOMATIC_BACKUP_CHECK
-
-Description:
- Check whether the Amazon ElastiCache Redis clusters have automatic backup turned on. The rule is NON_COMPLIANT if the SnapshotRetentionLimit of an Amazon Elasticache Redis cluster is 0.
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::ElastiCache::CacheCluster
-
-Rule Parameters:
- SnapshotRetentionPeriod
- (Optional) Minimum snapshot retention period in days for Amazon ElastiCache Redis cluster. Default is 15 days.
-
-Scenarios:
- Scenario: 1
- Given: No Amazon ElastiCache Redis cluster in the AWS Account
- Then: Return "NOT_APPLICABLE"
- Scenario: 2
- Given: Parameter SnapshotRetentionPeriod is configured
- And: It is not a positive integer greater then 0
- Then: Return an error
- Scenario: 3
- Given: At least 1 Amazon Elasticache Redis cluster is present
- And: The SnapshotRetentionLimit is set to 0
- Then: Return NON_COMPLIANT with Annotation "Automatic backup not enabled for Amazon ElastiCache cluster {Cluster_ID}"
- Scenario: 4
- Given: At least 1 Amazon Elasticache Redis cluster is present
- And: The SnapshotRetentionLimit is less than SnapshotRetentionPeriod
- Then: Return NON_COMPLAINT with Annotation "Automatic backup retention period for Amazon ElastiCache cluster {Cluster_ID} is less then {SnapshotRetentionPeriod} day(s)."
- Scenario: 5
- Given: At least 1 Amazon Elasticache Redis cluster is present
- And: The SnapshotRetentionLimit is greater than or equal to SnapshotRetentionPeriod
- Then: Return COMPLAINT
-'''
-import json
-import sys
-import datetime
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::ElastiCache::CacheCluster'
-DEFAULT_PARAMETER_VALUE = 15
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def get_replication_groups(ec_client):
- replication_groups = []
- marker = None
- replication_groups_result = {}
- while True:
- if not marker:
- replication_groups_result = ec_client.describe_replication_groups(MaxRecords=100)
- else:
- replication_groups_result = ec_client.describe_replication_groups(Marker=marker, MaxRecords=100)
- replication_groups.extend(replication_groups_result['ReplicationGroups'])
- if 'Marker' in replication_groups_result:
- marker = replication_groups_result['Marker']
- else:
- return replication_groups
-
-def get_cache_clusters(ec_client):
- cache_clusters = []
- marker = None
- cache_clusters_result = {}
- while True:
- if not marker:
- cache_clusters_result = ec_client.describe_cache_clusters(MaxRecords=100, ShowCacheNodeInfo=False, ShowCacheClustersNotInReplicationGroups=True)
- else:
- cache_clusters_result = ec_client.describe_cache_clusters(Marker=marker, MaxRecords=100, ShowCacheNodeInfo=False, ShowCacheClustersNotInReplicationGroups=True)
- for cluster in cache_clusters_result['CacheClusters']:
- if cluster['Engine'] == 'redis':
- cache_clusters.append(cluster)
- if 'Marker' in cache_clusters_result:
- marker = cache_clusters_result['Marker']
- else:
- return cache_clusters
-
-def generate_evaluations(eval_list, key, snapshot_retention_period, event):
- evaluations = []
- for cluster in eval_list:
- if cluster['SnapshotRetentionLimit'] == 0:
- evaluations.append(build_evaluation(cluster[key], 'NON_COMPLIANT', event, annotation="Automatic backup not enabled for Amazon ElastiCache cluster: {}".format(cluster[key])))
- elif cluster['SnapshotRetentionLimit'] < snapshot_retention_period:
- evaluations.append(build_evaluation(cluster[key], 'NON_COMPLIANT', event, annotation='Automatic backup retention period for Amazon ElastiCache cluster {} is less then {} day(s).'.format(cluster[key], snapshot_retention_period)))
- else:
- evaluations.append(build_evaluation(cluster[key], 'COMPLIANT', event))
- return evaluations
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- ec_client = get_client('elasticache', event)
- cache_clusters = get_cache_clusters(ec_client)
- evaluations = []
- replication_groups = get_replication_groups(ec_client)
- if not cache_clusters and not replication_groups:
- return build_evaluation(event['accountId'], "NOT_APPLICABLE", event)
- evaluations.extend(generate_evaluations(cache_clusters, 'CacheClusterId', valid_rule_parameters['SnapshotRetentionPeriod'], event))
- evaluations.extend(generate_evaluations(replication_groups, 'ReplicationGroupId', valid_rule_parameters['SnapshotRetentionPeriod'], event))
- return evaluations
-
-def evaluate_parameters(rule_parameters):
- if 'SnapshotRetentionPeriod' not in rule_parameters:
- return {'SnapshotRetentionPeriod': DEFAULT_PARAMETER_VALUE}
- if int(rule_parameters['SnapshotRetentionPeriod']) < 1:
- raise ValueError('SnapshotRetentionPeriod value should be an integer greater than 0')
- return {'SnapshotRetentionPeriod': int(rule_parameters['SnapshotRetentionPeriod'])}
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return status in (['OK', 'ResourceDiscovered']) and not event_left_scope
- # return (status == 'OK' or status == 'ResourceDiscovered') and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK/ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK_test.py b/vendor/github.com/awslabs/aws-config-rules/python/ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK/ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK_test.py
deleted file mode 100644
index ab8a265..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK/ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK_test.py
+++ /dev/null
@@ -1,195 +0,0 @@
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::ElastiCache::CacheCluster'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-EC_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- sample = 123
- def client(self, client_name, *args, **kwargs):
- self.sample = 123
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 'elasticache':
- return EC_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK')
-
-
-def replication_groups_se(**kwargs):
- if 'Marker' not in kwargs:
- return {'ReplicationGroups': [{'ReplicationGroupId':'ABC', 'SnapshotRetentionLimit': 16}], 'Marker': 'ABC'}
- return {'ReplicationGroups': [{'ReplicationGroupId':'DEF', 'SnapshotRetentionLimit': 10}]}
-
-
-class CompliantResourceTest(unittest.TestCase):
-
- def test_scenario_5_is_compliant(self):
- EC_CLIENT_MOCK.describe_cache_clusters = MagicMock(return_value={'CacheClusters': [{'CacheClusterId':'GHI', 'SnapshotRetentionLimit': 16, 'Engine': 'redis'}]})
- EC_CLIENT_MOCK.describe_replication_groups.side_effect = replication_groups_se
- lambda_result = RULE.lambda_handler(build_lambda_scheduled_event('{"SnapshotRetentionPeriod":"15"}'), {})
- print(lambda_result)
- assert_successful_evaluation(self, lambda_result, [build_expected_response('COMPLIANT', "GHI"),
- build_expected_response('COMPLIANT', "ABC"),
- build_expected_response('NON_COMPLIANT', "DEF", annotation="Automatic backup retention period for Amazon ElastiCache cluster DEF is less then 15 day(s).")
- ], len(lambda_result))
-
-
-class NonCompliantResourceTest(unittest.TestCase):
-
- def test_scenario_4_low_retention(self):
- EC_CLIENT_MOCK.describe_cache_clusters = MagicMock(return_value={'CacheClusters': [{'CacheClusterId':'ABC', 'SnapshotRetentionLimit': 16, 'Engine': 'redis'}]})
- EC_CLIENT_MOCK.describe_replication_groups = MagicMock(return_value={'ReplicationGroups': []})
- lambda_result = RULE.lambda_handler(build_lambda_scheduled_event('{"SnapshotRetentionPeriod":"15"}'), {})
- assert_successful_evaluation(self, lambda_result, [build_expected_response("COMPLIANT", "ABC")], len(lambda_result))
-
- def test_scenario_3_no_auto_backup(self):
- # compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None
- EC_CLIENT_MOCK.describe_cache_clusters = MagicMock(return_value={'CacheClusters': [{'CacheClusterId':'ABCD', 'SnapshotRetentionLimit': 0, 'Engine': 'redis'}]})
- EC_CLIENT_MOCK.describe_replication_groups = MagicMock(return_value={'ReplicationGroups': []})
- lambda_result = RULE.lambda_handler(build_lambda_scheduled_event('{"SnapshotRetentionPeriod":"15"}'), {})
- assert_successful_evaluation(self, lambda_result, [build_expected_response("NON_COMPLIANT", "ABCD", annotation="Automatic backup not enabled for Amazon ElastiCache cluster: ABCD")], len(lambda_result))
-
-class ErrorTest(unittest.TestCase):
-
- def test_scenario_2_parameter_error(self):
- lambda_result = RULE.lambda_handler(build_lambda_scheduled_event('{"SnapshotRetentionPeriod":"-1"}'), {})
- assert_customer_error_response(self, lambda_result, customer_error_message='SnapshotRetentionPeriod value should be an integer greater than 0', customer_error_code='InvalidParameterValueException')
-
-class NotApplicableResourceTest(unittest.TestCase):
-
- def test_scenario_1_no_resources(self):
- EC_CLIENT_MOCK.describe_cache_clusters = MagicMock(return_value={'CacheClusters': []})
- EC_CLIENT_MOCK.describe_replication_groups = MagicMock(return_value={'ReplicationGroups': []})
- lambda_result = RULE.lambda_handler(build_lambda_scheduled_event('{"SnapshotRetentionPeriod":"15"}'), {})
- assert_successful_evaluation(self, lambda_result, [build_expected_response("NOT_APPLICABLE", "123456789012")], len(lambda_result))
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK/parameters.json
deleted file mode 100644
index a4c4b33..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK",
- "SourceRuntime": "python3.6",
- "CodeKey": "ELASTICACHE_REDIS_CLUSTER_AUTO_BACKUP_CHECK.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{\"snapshotRetentionPeriod\": \"\"}",
- "SourcePeriodic": "TwentyFour_Hours"
- },
- "Tags": "[]"
-}
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELASTICSEARCH_ENCRYPTED_AT_REST/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/ELASTICSEARCH_ENCRYPTED_AT_REST/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELASTICSEARCH_ENCRYPTED_AT_REST/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELASTICSEARCH_ENCRYPTED_AT_REST/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/ELASTICSEARCH_ENCRYPTED_AT_REST/parameters.json
deleted file mode 100644
index a8f1161..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELASTICSEARCH_ENCRYPTED_AT_REST/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "ELASTICSEARCH_ENCRYPTED_AT_REST",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "ELASTICSEARCH_ENCRYPTED_AT_REST"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELASTICSEARCH_IN_VPC_ONLY/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/ELASTICSEARCH_IN_VPC_ONLY/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELASTICSEARCH_IN_VPC_ONLY/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELASTICSEARCH_IN_VPC_ONLY/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/ELASTICSEARCH_IN_VPC_ONLY/parameters.json
deleted file mode 100644
index f55bebc..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELASTICSEARCH_IN_VPC_ONLY/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "ELASTICSEARCH_IN_VPC_ONLY",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "ELASTICSEARCH_IN_VPC_ONLY"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELB_ACM_CERTIFICATE_REQUIRED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/ELB_ACM_CERTIFICATE_REQUIRED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELB_ACM_CERTIFICATE_REQUIRED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELB_ACM_CERTIFICATE_REQUIRED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/ELB_ACM_CERTIFICATE_REQUIRED/parameters.json
deleted file mode 100644
index 47c58c5..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELB_ACM_CERTIFICATE_REQUIRED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "ELB_ACM_CERTIFICATE_REQUIRED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::ElasticLoadBalancing::LoadBalancer",
- "SourceIdentifier": "ELB_ACM_CERTIFICATE_REQUIRED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELB_ALB_PREDEFINED_SSL_CHECK/ELB_ALB_PREDEFINED_SSL_CHECK.py b/vendor/github.com/awslabs/aws-config-rules/python/ELB_ALB_PREDEFINED_SSL_CHECK/ELB_ALB_PREDEFINED_SSL_CHECK.py
deleted file mode 100644
index c2d62f2..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELB_ALB_PREDEFINED_SSL_CHECK/ELB_ALB_PREDEFINED_SSL_CHECK.py
+++ /dev/null
@@ -1,456 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-'''
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- ELB_ALB_PREDEFINED_SSL_CHECK
-
-Description:
- Checks whether your Application Load Balancer TLS/SSL listeners are using a or several predefined policy. The rule is only applicable if there are TLS/SSL listeners for the Application Load Balancer.
-
-Trigger:
- Configuration change on AWS::LoadBalancingV2::LoadBalancer
-
-Resource Type to report on:
- AWS::ElasticLoadBalancingV2::LoadBalancer
-
-Rule Parameters:
- | ---------------------- | ---------- | -------------------------------------------------------------- |
- | Parameter Name | Type | Description |
- | ---------------------- | ---------- | -------------------------------------------------------------- |
- | ValidPolicies | Mandatory | List of TLS/SSL policy name which are valid, separated with comma. |
- | ---------------------- | ---------- | -------------------------------------------------------------- |
-
-Feature:
- In order to: ensure that the traffic is encrypted in transit with the proper method
- As: a Security Officer
- I want: to verify that the configuration of any listener TLS/SSL policy of an application load balancer is correct.
-
-Scenarios:
- Scenario 1:
- Given: the ALB has no HTTPS listeners
- then: Return NOT_APPLICABLE
-
- Scenario 2:
- And: the ALB has at least 1 HTTPS listener
- And: At least 1 listener has not their TLS/SSL Policy name listed in the ValidPolicies parameter
- then: Return NON_COMPLIANT
-
- Scenario 3:
- And: the ALB has at least 1 HTTPS listener
- And: All listeners have their TLS/SSL Policy name listed in the ValidPolicies parameter
- then: Return COMPLIANT
-'''
-import json
-import datetime
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::ElasticLoadBalancingV2::LoadBalancer'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
- evaluations = []
- alb_client = get_client("elbv2", event)
-
- all_elbv2 = get_all_elbv2(alb_client)
-
- for elb in all_elbv2:
- if elb['Type'] != 'application':
- continue
-
- alb_all_listeners = get_all_listeners(alb_client, elb['LoadBalancerArn'])
-
- if not is_https_listener(alb_all_listeners):
- evaluations.append(build_evaluation(elb['LoadBalancerArn'], 'NOT_APPLICABLE', event))
- continue
-
- https_bool, https_str = is_all_https_listeners_compliant(alb_all_listeners, valid_rule_parameters)
- if not https_bool:
- evaluations.append(build_evaluation(elb['LoadBalancerArn'], 'NON_COMPLIANT', event, annotation=https_str))
- continue
-
- evaluations.append(build_evaluation(elb['LoadBalancerArn'], 'COMPLIANT', event))
-
- return evaluations
-
-def is_https_listener(listeners):
- for listener in listeners:
- if 'SslPolicy' in listener.keys():
- return True
- return False
-
-def is_all_https_listeners_compliant(listeners, parameters):
- for listener in listeners:
- if 'SslPolicy' in listener.keys():
- if listener['SslPolicy'] not in parameters['ValidPolicies']:
- return False, 'This ALB has a HTTPS listener with a TLS/SSL policy ({}) not listed in the ValidPolicies parameter ({}).'.format(listener['SslPolicy'], ', '.join(parameters['ValidPolicies']))
- return True, None
-
-def get_all_elbv2(client):
- resp = client.describe_load_balancers(PageSize=400)
- print(resp)
- items = []
- while resp:
- items += resp['LoadBalancers']
- resp = client.describe_load_balancers(Marker=resp['NextMarker']) if 'NextMarker' in resp else None
- return items
-
-def get_all_listeners(client, elbv2_arn):
- resp = client.describe_listeners(LoadBalancerArn=elbv2_arn, PageSize=400)
- items = []
- while resp:
- items += resp['Listeners']
- resp = client.describe_listeners(LoadBalancerArn=elbv2_arn, Marker=resp['NextMarker']) if 'NextMarker' in resp else None
- return items
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- valid_rule_parameters = {}
-
- if 'ValidPolicies' not in rule_parameters:
- raise ValueError('The parameter "ValidPolicies" must be configured.')
-
- param_list = rule_parameters['ValidPolicies']
- param_list = param_list.split(',')
-
- for index, item in enumerate(param_list):
- param_list[index] = item.strip()
-
- valid_rule_parameters['ValidPolicies'] = param_list
-
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Parameter value is invalid",
- internalErrorDetails="An ValueError was raised during the validation of the Parameter value",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluations, ResultToken=resultToken, TestMode=testMode)
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELB_ALB_PREDEFINED_SSL_CHECK/ELB_ALB_PREDEFINED_SSL_CHECK_test.py b/vendor/github.com/awslabs/aws-config-rules/python/ELB_ALB_PREDEFINED_SSL_CHECK/ELB_ALB_PREDEFINED_SSL_CHECK_test.py
deleted file mode 100644
index d5e9e34..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELB_ALB_PREDEFINED_SSL_CHECK/ELB_ALB_PREDEFINED_SSL_CHECK_test.py
+++ /dev/null
@@ -1,216 +0,0 @@
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::ElasticLoadBalancingV2::LoadBalancer'
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-elbv2_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'sts':
- return sts_client_mock
- elif client_name == 'elbv2':
- return elbv2_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('ELB_ALB_PREDEFINED_SSL_CHECK')
-
-class TestParameter(unittest.TestCase):
-
- rule_parameters_empty = '{}'
- rule_parameters_one = '{"ValidPolicies":"Some_policy_1"}'
- rule_parameters_two = '{"ValidPolicies":"Some_policy_1, Some_policy_2"}'
-
- def test_validpolicy_mandatory_parameter(self):
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException', 'The parameter "ValidPolicies" must be configured.')
-
-class TestCompliance(unittest.TestCase):
-
- rule_parameters_two = '{"ValidPolicies":"Some_policy_1, Some_policy_2"}'
- describe_listeners_no_https = {'Listeners': [{ 'ListenerArn': 'arn1'}]}
- describe_listeners_compliant = {'Listeners': [{ 'ListenerArn': 'arn1', 'SslPolicy': 'Some_policy_1'},{ 'ListenerArn': 'arn2', 'SslPolicy': 'Some_policy_2'}]}
- describe_listeners_non_compliant = {'Listeners': [{ 'ListenerArn': 'arn2', 'SslPolicy': 'Some_policy_3'}]}
- describe_load_balancers_not_app = {'LoadBalancers': [{ 'LoadBalancerArn': 'arn1', 'Type': 'other'}]}
- describe_load_balancers_app = {'LoadBalancers': [{ 'LoadBalancerArn': 'arn1', 'Type': 'application'},{ 'LoadBalancerArn': 'arn2', 'Type': 'application'}]}
-
- def test_scenario0_no_elb(self):
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_scheduled_event(self.rule_parameters_two), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario0_no_app_elb(self):
- rule.ASSUME_ROLE_MODE = False
- elbv2_client_mock.describe_load_balancers = MagicMock(return_value=self.describe_load_balancers_not_app)
- elbv2_client_mock.describe_listeners = MagicMock(return_value=self.describe_listeners_no_https)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.rule_parameters_two), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario1_no_https_listener(self):
- rule.ASSUME_ROLE_MODE = False
- elbv2_client_mock.describe_load_balancers = MagicMock(return_value=self.describe_load_balancers_app)
- elbv2_client_mock.describe_listeners = MagicMock(return_value=self.describe_listeners_no_https)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.rule_parameters_two), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', 'arn1'))
- resp_expected.append(build_expected_response('NOT_APPLICABLE', 'arn2'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- def test_scenario2_no_compliant_https(self):
- rule.ASSUME_ROLE_MODE = False
- elbv2_client_mock.describe_load_balancers = MagicMock(return_value=self.describe_load_balancers_app)
- elbv2_client_mock.describe_listeners = MagicMock(return_value=self.describe_listeners_non_compliant)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.rule_parameters_two), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'arn1', annotation='This ALB has a HTTPS listener with a TLS/SSL policy (Some_policy_3) not listed in the ValidPolicies parameter (Some_policy_1, Some_policy_2).'))
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'arn2', annotation='This ALB has a HTTPS listener with a TLS/SSL policy (Some_policy_3) not listed in the ValidPolicies parameter (Some_policy_1, Some_policy_2).'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- def test_scenario3_compliant_https(self):
- rule.ASSUME_ROLE_MODE = False
- elbv2_client_mock.describe_load_balancers = MagicMock(return_value=self.describe_load_balancers_app)
- elbv2_client_mock.describe_listeners = MagicMock(return_value=self.describe_listeners_compliant)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.rule_parameters_two), {})
- print(response)
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'arn1'))
- resp_expected.append(build_expected_response('COMPLIANT', 'arn2'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(testClass, response, customerErrorCode=None, customerErrorMessage=None):
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- sts_client_mock.reset_mock(return_value=True)
- sts_client_mock.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- rule_parameters_one = '{"ValidPolicies":"some_policy_1"}'
-
- def test_sts_unknown_error(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}', self.rule_parameters_one), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}', self.rule_parameters_one), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELB_ALB_PREDEFINED_SSL_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/ELB_ALB_PREDEFINED_SSL_CHECK/parameters.json
deleted file mode 100644
index c2e3137..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELB_ALB_PREDEFINED_SSL_CHECK/parameters.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "ELB_ALB_PREDEFINED_SSL_CHECK",
- "SourceRuntime": "python3.6",
- "CodeKey": "ELB_ALB_PREDEFINED_SSL_CHECK.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::ElasticLoadBalancingV2::LoadBalancer",
- "RuleSets": [
- "rulecriticity:high",
- "pci",
- "pci:4.1",
- "elb",
- "alb"
- ]
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELB_CUSTOM_SECURITY_POLICY_SSL_CHECK/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/ELB_CUSTOM_SECURITY_POLICY_SSL_CHECK/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELB_CUSTOM_SECURITY_POLICY_SSL_CHECK/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELB_CUSTOM_SECURITY_POLICY_SSL_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/ELB_CUSTOM_SECURITY_POLICY_SSL_CHECK/parameters.json
deleted file mode 100644
index c7e24bb..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELB_CUSTOM_SECURITY_POLICY_SSL_CHECK/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "ELB_CUSTOM_SECURITY_POLICY_SSL_CHECK",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{\"sslProtocolsAndCiphers\": \"\"}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::ElasticLoadBalancing::LoadBalancer",
- "SourceIdentifier": "ELB_CUSTOM_SECURITY_POLICY_SSL_CHECK"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELB_LOGGING_ENABLED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/ELB_LOGGING_ENABLED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELB_LOGGING_ENABLED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELB_LOGGING_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/ELB_LOGGING_ENABLED/parameters.json
deleted file mode 100644
index 14a10e1..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELB_LOGGING_ENABLED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "ELB_LOGGING_ENABLED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{\"s3BucketNames\": \"\"}",
- "SourceEvents": "AWS::ElasticLoadBalancing::LoadBalancer,AWS::ElasticLoadBalancingV2::LoadBalancer",
- "SourceIdentifier": "ELB_LOGGING_ENABLED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELB_PREDEFINED_SECURITY_POLICY_SSL_CHECK/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/ELB_PREDEFINED_SECURITY_POLICY_SSL_CHECK/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELB_PREDEFINED_SECURITY_POLICY_SSL_CHECK/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ELB_PREDEFINED_SECURITY_POLICY_SSL_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/ELB_PREDEFINED_SECURITY_POLICY_SSL_CHECK/parameters.json
deleted file mode 100644
index befcdaa..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ELB_PREDEFINED_SECURITY_POLICY_SSL_CHECK/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "ELB_PREDEFINED_SECURITY_POLICY_SSL_CHECK",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{\"predefinedPolicyName\": \"\"}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::ElasticLoadBalancing::LoadBalancer",
- "SourceIdentifier": "ELB_PREDEFINED_SECURITY_POLICY_SSL_CHECK"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EMR_KERBEROS_ENABLED/EMR_KERBEROS_ENABLED.py b/vendor/github.com/awslabs/aws-config-rules/python/EMR_KERBEROS_ENABLED/EMR_KERBEROS_ENABLED.py
deleted file mode 100644
index db570ed..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EMR_KERBEROS_ENABLED/EMR_KERBEROS_ENABLED.py
+++ /dev/null
@@ -1,537 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-"""
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- emr_kerberos_enabled
-
-Description:
- Checks that EMR clusters have Kerberos Enabled
-
-Trigger:
- Periodic
-
-Resource Type to report on:
- AWS::EMR::Cluster
-
-Rule Parameters:
- | ---------------------| --------- | --------------------------------------------- | ------------------------------ |
- | Parameter Name | Type | Description | Notes |
- | ---------------------| --------- | --------------------------------------------- | ------------------------------ |
- | TicketLifetimeInHours| Optional | Period for which Kerberos ticket issued by | must be equal to or less than |
- | | | cluster's KDC is valid | parameter |
- | ---------------------| --------- | --------------------------------------------- | ------------------------------ |
- | Realm | Optional | Kereberos Realm name of the other realm in | must be equal to parameter |
- | | | the trust relationship | |
- | ---------------------| --------- | --------------------------------------------- | ------------------------------ |
- | Domain | Optional | Domain name of the other realm in the trust | must be equal to parameter |
- | | | relationship | |
- | ---------------------| --------- | --------------------------------------------- | ------------------------------ |
- | AdminServer | Optional | Fully qualified domain of the admin server in | must be equal to parameter |
- | | | the other realm of the trust relationship | |
- | ---------------------| --------- | --------------------------------------------- | ------------------------------ |
- | KdcServer | Optional | Fully qualified domain of the KDC server in | must be equal to parameter |
- | | | the other realm of the trust relationship | |
- | ---------------------| --------- | --------------------------------------------- | ------------------------------ |
-
-Feature:
- In order to: enforce our Security Policies
- As: a Security Officer
- I want: to ensure that EMR Clusters have Kerberos authentication enabled
-
-Scenarios:
- Scenario 1:
- Given: The parameter TicketLifetimeInHours is configured
- And: the parameter is not a numeric value
- Then: Return an error
-
- Scenario 2:
- Given: The parameter Realm is configured
- And: the parameter is not in capital letters
- Then: Return an error
-
- Scenario 3:
- Given: The parameter Domain is configured
- And: the parameter is not valid
- Then: Return an error
-
- Scenario 4:
- Given: The parameter AdminServer is configured
- And: the parameter is not an alphanumeric value
- Then: Return an error
-
- Scenario 5:
- Given: The parameter KdcServer is configured
- And: the parameter is not an alphanumeric value
- Then: Return an error
-
- Scenario 6:
- Given: An EMR cluster is terminating, terminated or terminated with errors
- Then: Do not evaluate
-
- Scenario 7:
- Given: No EMR cluster is running
- Then: Return Compliant
-
- Scenario 8:
- Given: An EMR cluster is running
- And: No security configuration is attached to the cluster
- Then: Return Non_Compliant
-
- Scenario 9:
- Given: An EMR cluster is running
- And: Security configuration is attached to the cluster
- And: The Parameter TicketLifetimeInHours or Realm or Domain or AdminServer or KdcServer is configured and not valid
- Then: Return Non_Compliant
-
- Scenario 10:
- Given: An EMR cluster is running
- And: Security configuration is attached to the cluster
- And: The Parameter TicketLifetimeInHours or Realm or Domain or AdminServer or KdcServer is configured and valid
- Then: Return Compliant
-"""
-import json
-import datetime
-import re
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::EMR::Cluster'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
- evaluations = []
-
- emr_client = get_client('emr', event)
-
- cluster_list = get_all_cluster(emr_client)
-
- if not cluster_list:
- return None
-
- for cluster in cluster_list:
-
- cluster_id = cluster["Id"]
- described_cluster = emr_client.describe_cluster(ClusterId=cluster_id)["Cluster"]
-
- if described_cluster["Status"]["State"] in ["TERMINATING", "TERMINATED", "TERMINATED_WITH_ERRORS"]:
- evaluations.append(build_evaluation(cluster_id, 'NOT_APPLICABLE', event))
- continue
-
- if "SecurityConfiguration" not in described_cluster:
- evaluations.append(build_evaluation(cluster_id, 'NON_COMPLIANT', event, annotation='No Security Configuration is attached.'))
- continue
-
- cluster_sc_details = json.loads(emr_client.describe_security_configuration(
- Name=described_cluster["SecurityConfiguration"]
- )["SecurityConfiguration"])
-
- if "AuthenticationConfiguration" not in cluster_sc_details or \
- "KerberosConfiguration" not in cluster_sc_details["AuthenticationConfiguration"] or \
- "ClusterDedicatedKdcConfiguration" not in cluster_sc_details["AuthenticationConfiguration"]["KerberosConfiguration"]:
- evaluations.append(build_evaluation(cluster_id, 'NON_COMPLIANT', event, annotation='Kerberos Authentication is not enabled in the Security Configuration.'))
- continue
-
- sc_kerberos_details = cluster_sc_details["AuthenticationConfiguration"]["KerberosConfiguration"]["ClusterDedicatedKdcConfiguration"]
- if "TicketLifetimeInHours" in rule_parameters:
- if sc_kerberos_details["TicketLifetimeInHours"] < rule_parameters["TicketLifetimeInHours"]:
- evaluations.append(build_evaluation(cluster_id, 'NON_COMPLIANT', event, annotation='TicketLifetimeInHours is smaller than the specified Rule parameter TicketLifetimeInHours.'))
- continue
-
- if "CrossRealmTrustConfiguration" not in sc_kerberos_details:
- evaluations.append(build_evaluation(cluster_id, 'NON_COMPLIANT', event, annotation='CrossRealmTrustConfiguration is not configured in security configuration.'))
- continue
-
- sc_kerberos_details = sc_kerberos_details["CrossRealmTrustConfiguration"]
-
- if "Realm" in rule_parameters:
- if not str(sc_kerberos_details["Realm"]).__eq__(rule_parameters["Realm"]):
- evaluations.append(build_evaluation(cluster_id, 'NON_COMPLIANT', event, annotation='Realm is not equal to the specified Rule parameter Realm.'))
- continue
-
- if "Domain" in rule_parameters:
- if not str(sc_kerberos_details["Domain"]).__eq__(rule_parameters["Domain"]):
- evaluations.append(build_evaluation(cluster_id, 'NON_COMPLIANT', event, annotation='Domain is not equal to the specified Rule parameter Domain.'))
- continue
-
- if "AdminServer" in rule_parameters:
- if not str(sc_kerberos_details["AdminServer"]).__eq__(rule_parameters["AdminServer"]):
- evaluations.append(build_evaluation(cluster_id, 'NON_COMPLIANT', event, annotation='AdminServer is not equal to the specified Rule parameter AdminServer.'))
- continue
-
- if "KdcServer" in rule_parameters:
- if not str(sc_kerberos_details["KdcServer"]).__eq__(rule_parameters["KdcServer"]):
- evaluations.append(build_evaluation(cluster_id, 'NON_COMPLIANT', event, annotation='KdcServer is not equal to the specified Rule parameter KdcServer.'))
- continue
-
- evaluations.append(build_evaluation(cluster_id, 'COMPLIANT', event))
- continue
-
- return evaluations
-
-def get_all_cluster(client):
- clusters = client.list_clusters()
- all_clusters = []
- while True:
- all_clusters += clusters['Clusters']
- if "Marker" in clusters:
- clusters = client.list_clusters(Marker=clusters["Marker"])
- else:
- break
- return all_clusters
-
-def evaluate_parameters(rule_parameters):
-
- if not rule_parameters:
- return {}
- if "TicketLifetimeInHours" in rule_parameters and not str(rule_parameters["TicketLifetimeInHours"]).isnumeric():
- raise ValueError("TicketLifetimeInHours")
- if "Realm" in rule_parameters and (not str(rule_parameters["Realm"]).isupper() or not is_valid_hostname(
- rule_parameters["Realm"], "DOMAIN")):
- raise ValueError("Realm")
- if "Domain" in rule_parameters and not is_valid_hostname(rule_parameters["Domain"], "DOMAIN"):
- raise ValueError("Domain")
- if "AdminServer" in rule_parameters and not is_valid_hostname(rule_parameters["AdminServer"], "FQDN"):
- raise ValueError("AdminServer")
- if "KdcServer" in rule_parameters and not is_valid_hostname(rule_parameters["KdcServer"], "FQDN"):
- raise ValueError("KdcServer")
- return rule_parameters
-
-def is_valid_hostname(hostname, type):
- if len(hostname) > 255:
- return False
- if hostname[-1] == ".":
- hostname = hostname[:-1] # strip exactly one dot from the right, if present
- if str(type).__eq__("FQDN"):
- fqdn = hostname.split(":")
- if len(fqdn) > 1 and not str(fqdn[1]).isnumeric():
- return False
- hostname = fqdn[0]
- if len(hostname.split(".")) < 2:
- return False
- allowed = re.compile("(?!-)[A-Z\d-]{1,63}(? 256:
- return annotation_string[:244] + " [truncated]"
- return annotation_string
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
-
- return status in ('OK', 'ResourceDiscovered') and not event_left_scope
-
-
-def get_assume_role_credentials(role_arn, region=None):
- sts_client = boto3.client('sts', region)
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EMR_SECURITY_GROUPS_RESTRICTED/EMR_SECURITY_GROUPS_RESTRICTED_test.py b/vendor/github.com/awslabs/aws-config-rules/python/EMR_SECURITY_GROUPS_RESTRICTED/EMR_SECURITY_GROUPS_RESTRICTED_test.py
deleted file mode 100644
index 5170b04..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EMR_SECURITY_GROUPS_RESTRICTED/EMR_SECURITY_GROUPS_RESTRICTED_test.py
+++ /dev/null
@@ -1,303 +0,0 @@
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::EMR::Cluster'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-EMR_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 'emr':
- return EMR_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('EMR_SECURITY_GROUPS_RESTRICTED')
-
-class ComplianceTest(unittest.TestCase):
-
- cluster_list = {
- 'Clusters': [{
- 'Id': 'j-AAAAA0AAAAA',
- 'Status': {
- 'State': 'RUNNING'
- }
- }, {
- 'Id': 'j-AAAAA000000',
- 'Status': {
- 'State': 'WAITING'
- }
- }, {
- 'Id': 'j-AAAAA0BBBBB',
- 'Status': {
- 'State': 'RUNNING'
- }
- }]
- }
-
- described_clusters = [{
- "Cluster": {
- "Id": "j-AAAAA0AAAAA",
- "Ec2InstanceAttributes": {
- "EmrManagedMasterSecurityGroup": "sg-1111aaaa",
- "EmrManagedSlaveSecurityGroup": "sg-2222bbbb",
- "AdditionalMasterSecurityGroups": [],
- "AdditionalSlaveSecurityGroups": []
- }
- }
- }, {
- "Cluster": {
- "Id": "j-AAAAA000000",
- "Ec2InstanceAttributes": {
- "EmrManagedMasterSecurityGroup": "sg-3333cccc",
- "EmrManagedSlaveSecurityGroup": "sg-4444dddd",
- "AdditionalMasterSecurityGroups": [
- "sg-1111aaaa"
- ],
- "AdditionalSlaveSecurityGroups": [
- "sg-2222bbbb"
- ]
- }
- }
- }, {
- "Cluster": {
- "Id": "j-AAAAA0BBBBB",
- "Ec2InstanceAttributes": {
- "EmrManagedMasterSecurityGroup": "sg-1111aaaa",
- "EmrManagedSlaveSecurityGroup": "sg-2222bbbb",
- "AdditionalMasterSecurityGroups": [
- "sg-3333cccc",
- "sg-4444dddd"
- ],
- "AdditionalSlaveSecurityGroups": []
- }
- }
- }]
-
- #Scenario 1: No RUNNING and WAITING clusters
- def test_1_no_clusters(self):
- no_clusters = {
- "Clusters": []
- }
- EMR_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": EMR_CLIENT_MOCK,
- "paginate.return_value": [no_clusters]})
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = [build_expected_response('NOT_APPLICABLE', compliance_resource_id='123456789012', compliance_resource_type="AWS::::Account")]
- assert_successful_evaluation(self, response, resp_expected)
-
- #Scenario 2: Security group(s) for the Amazon EMR cluster has a rule with IP range 0.0.0.0/0 or ::/0
- def test_2_security_groups_open(self):
-
- security_groups_config_items = {
- "baseConfigurationItems": [{
- "configuration": "{\"ipPermissions\": [{\"ipv4Ranges\": [{\"cidrIp\": \"0.0.0.0/0\"}],\"ipv6Ranges\": []}],\"groupId\": \"sg-1111aaaa\"}"
- }, {
- "configuration": "{\"ipPermissions\": [{\"ipv4Ranges\": [{\"cidrIp\": \"1.1.1.1/32\"}],\"ipv6Ranges\": [{\"cidrIpv6\": \"::/0\"}]}],\"groupId\": \"sg-2222bbbb\"}"
- }, {
- "configuration": "{\"ipPermissions\": [{\"ipv4Ranges\": [],\"ipv6Ranges\": [{\"cidrIpv6\": \"::/0\"}]}],\"groupId\": \"sg-3333cccc\"}"
- }, {
- "configuration": "{\"ipPermissions\": [{\"ipv4Ranges\": [{\"cidrIp\": \"0.0.0.0/0\"}],\"ipv6Ranges\": [{\"cidrIpv6\": \"::/0\"}]}],\"groupId\": \"sg-4444dddd\"}"
- }],
- "unprocessedResourceKeys": []
- }
-
- EMR_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": EMR_CLIENT_MOCK,
- "paginate.return_value": [self.cluster_list]})
- EMR_CLIENT_MOCK.describe_cluster = MagicMock(side_effect=self.described_clusters)
- CONFIG_CLIENT_MOCK.batch_get_resource_config = MagicMock(side_effect=[security_groups_config_items])
-
- resp_expected = [build_expected_response('NON_COMPLIANT', compliance_resource_id='j-AAAAA0AAAAA', annotation="This Amazon EMR cluster has one or more Security Groups open to the world."),
- build_expected_response('NON_COMPLIANT', compliance_resource_id='j-AAAAA000000', annotation="This Amazon EMR cluster has one or more Security Groups open to the world."),
- build_expected_response('NON_COMPLIANT', compliance_resource_id='j-AAAAA0BBBBB', annotation="This Amazon EMR cluster has one or more Security Groups open to the world.")]
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- assert_successful_evaluation(self, response, resp_expected, 3)
-
- #Scenario 3: Security group(s) for the Amazon EMR cluster do not have a rule with IP range 0.0.0.0/0 or ::/0
- def test_3_security_groups_closed(self):
-
- security_groups_config_items = {
- "baseConfigurationItems": [{
- "configuration": "{\"ipPermissions\": [{\"ipv4Ranges\": [{\"cidrIp\": \"2.2.2.2/32\"}],\"ipv6Ranges\": []}],\"groupId\": \"sg-1111aaaa\"}"
- }, {
- "configuration": "{\"ipPermissions\": [{\"ipv4Ranges\": [{\"cidrIp\": \"1.1.1.1/32\"}],\"ipv6Ranges\": [{\"cidrIpv6\": \"1111:1111::/128\"}]}],\"groupId\": \"sg-2222bbbb\"}"
- }, {
- "configuration": "{\"ipPermissions\": [{\"ipv4Ranges\": [],\"ipv6Ranges\": [{\"cidrIpv6\": \"2222:2222::/128\"}]}],\"groupId\": \"sg-3333cccc\"}"
- }, {
- "configuration": "{\"ipPermissions\": [{\"ipv4Ranges\": [{\"cidrIp\": \"2.2.2.2/32\"}],\"ipv6Ranges\": [{\"cidrIpv6\": \"222:2222::/128\"}]}],\"groupId\": \"sg-4444dddd\"}"
- }],
- "unprocessedResourceKeys": []
- }
-
- EMR_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": EMR_CLIENT_MOCK,
- "paginate.return_value": [self.cluster_list]})
- EMR_CLIENT_MOCK.describe_cluster = MagicMock(side_effect=self.described_clusters)
- CONFIG_CLIENT_MOCK.batch_get_resource_config = MagicMock(side_effect=[security_groups_config_items])
-
- resp_expected = [build_expected_response('COMPLIANT', compliance_resource_id='j-AAAAA0AAAAA'),
- build_expected_response('COMPLIANT', compliance_resource_id='j-AAAAA000000'),
- build_expected_response('COMPLIANT', compliance_resource_id='j-AAAAA0BBBBB')]
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- assert_successful_evaluation(self, response, resp_expected, 3)
-
- #Scenario 2 & 3 mixed
- def test_2_3_security_groups_mixed(self):
-
- security_groups_config_items = {
- "baseConfigurationItems": [{
- "configuration": "{\"ipPermissions\": [{\"ipv4Ranges\": [{\"cidrIp\": \"2.2.2.2/32\"}],\"ipv6Ranges\": []}],\"groupId\": \"sg-1111aaaa\"}"
- }, {
- "configuration": "{\"ipPermissions\": [{\"ipv4Ranges\": [{\"cidrIp\": \"1.1.1.1/32\"}],\"ipv6Ranges\": [{\"cidrIpv6\": \"1111:1111::/128\"}]}],\"groupId\": \"sg-2222bbbb\"}"
- }, {
- "configuration": "{\"ipPermissions\": [{\"ipv4Ranges\": [],\"ipv6Ranges\": [{\"cidrIpv6\": \"::/0\"}]}],\"groupId\": \"sg-3333cccc\"}"
- }, {
- "configuration": "{\"ipPermissions\": [{\"ipv4Ranges\": [{\"cidrIp\": \"0.0.0.0/0\"}],\"ipv6Ranges\": [{\"cidrIpv6\": \"::/0\"}]}],\"groupId\": \"sg-4444dddd\"}"
- }],
- "unprocessedResourceKeys": []
- }
-
- EMR_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": EMR_CLIENT_MOCK,
- "paginate.return_value": [self.cluster_list]})
- EMR_CLIENT_MOCK.describe_cluster = MagicMock(side_effect=self.described_clusters)
- CONFIG_CLIENT_MOCK.batch_get_resource_config = MagicMock(side_effect=[security_groups_config_items])
-
- resp_expected = [build_expected_response('COMPLIANT', compliance_resource_id='j-AAAAA0AAAAA'),
- build_expected_response('NON_COMPLIANT', compliance_resource_id='j-AAAAA000000', annotation="This Amazon EMR cluster has one or more Security Groups open to the world."),
- build_expected_response('NON_COMPLIANT', compliance_resource_id='j-AAAAA0BBBBB', annotation="This Amazon EMR cluster has one or more Security Groups open to the world.")]
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- assert_successful_evaluation(self, response, resp_expected, 3)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- RULE.evaluate_parameters = MagicMock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- RULE.evaluate_parameters = MagicMock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/EMR_SECURITY_GROUPS_RESTRICTED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/EMR_SECURITY_GROUPS_RESTRICTED/parameters.json
deleted file mode 100644
index a8951e2..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/EMR_SECURITY_GROUPS_RESTRICTED/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "EMR_SECURITY_GROUPS_RESTRICTED",
- "SourceRuntime": "python3.6",
- "CodeKey": "EMR_SECURITY_GROUPS_RESTRICTED.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourcePeriodic": "One_Hour"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ENTERPRISE_SUPPORT_PLAN_ENABLED/ENTERPRISE_SUPPORT_PLAN_ENABLED.py b/vendor/github.com/awslabs/aws-config-rules/python/ENTERPRISE_SUPPORT_PLAN_ENABLED/ENTERPRISE_SUPPORT_PLAN_ENABLED.py
deleted file mode 100644
index 3b7384b..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ENTERPRISE_SUPPORT_PLAN_ENABLED/ENTERPRISE_SUPPORT_PLAN_ENABLED.py
+++ /dev/null
@@ -1,397 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-'''
-#####################################
-## Gherkin ##
-#####################################
-Rule Name:
- ENTERPRISE_SUPPORT_PLAN_ENABLED
-Description:
- Check whether the Enterprise Support Plan is enabled for an AWS Account.
-Trigger:
- Periodic
-Reports on:
- AWS::::Account
-Rule Parameters:
- None
-Scenarios:
- Scenario: 1
- Given: AWS Support API call errors out with SubscriptionRequired exception while using the DescribeSeverityLevels API call
- Then: Return NON_COMPLIANT with Annotation "The AWS Enterprise Support Plan is not enabled for this AWS Account."
- Scenario: 2
- Given: The AWS Account does not have access to "critical" case severity level as returned by the AWS Support API DescribeSeverityLevels
- Then: Return NON_COMPLIANT with Annotation "The AWS Enterprise Support Plan is not enabled for this AWS Account."
- Scenario: 3
- Given: The AWS Account has access to "critical" case severity level as returned by the AWS Support API DescribeSeverityLevels
- Then: Return COMPLIANT
-'''
-
-import json
-import sys
-import datetime
-import boto3
-import botocore
-from botocore.exceptions import ClientError
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-# AWS Support API has only one endpoint which is in us-east-1 region
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- support_client = get_client('support', event, 'us-east-1')
- try:
- # Account with Basic or Developer support returns ClientError exception
- response = support_client.describe_severity_levels()
- except ClientError as error:
- if error.response['Error']['Code'] == 'SubscriptionRequiredException':
- return build_evaluation(
- event['accountId'],
- "NON_COMPLIANT",
- event,
- annotation="The AWS Enterprise Support Plan is not enabled for this AWS Account."
- )
- raise
-
- for level in response['severityLevels']:
- if level['code'] == 'critical':
- return build_evaluation(event['accountId'], "COMPLIANT", event)
-
- return build_evaluation(
- event['accountId'],
- "NON_COMPLIANT",
- event,
- annotation="The AWS Enterprise Support Plan is not enabled for this AWS Account."
- )
-
-def evaluate_parameters(rule_parameters):
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event, region=None):
- """Return the service boto client. It should be used instead of directly calling the client.
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service, region)
- credentials = get_assume_role_credentials(event["executionRoleArn"], region)
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken'],
- region_name=region
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None). It will be truncated to 255 if longer.
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = build_annotation(annotation)
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None). It will be truncated to 255 if longer.
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = build_annotation(annotation)
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Build annotation within Service constraints
-def build_annotation(annotation_string):
- if len(annotation_string) > 256:
- return annotation_string[:244] + " [truncated]"
- return annotation_string
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return status in ('OK', 'ResourceDiscovered') and not event_left_scope
-
-def get_assume_role_credentials(role_arn, region=None):
- sts_client = boto3.client('sts', region)
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ENTERPRISE_SUPPORT_PLAN_ENABLED/ENTERPRISE_SUPPORT_PLAN_ENABLED_test.py b/vendor/github.com/awslabs/aws-config-rules/python/ENTERPRISE_SUPPORT_PLAN_ENABLED/ENTERPRISE_SUPPORT_PLAN_ENABLED_test.py
deleted file mode 100644
index 2f59e3d..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ENTERPRISE_SUPPORT_PLAN_ENABLED/ENTERPRISE_SUPPORT_PLAN_ENABLED_test.py
+++ /dev/null
@@ -1,203 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-SUPPORT_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 'support':
- return SUPPORT_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('ENTERPRISE_SUPPORT_PLAN_ENABLED')
-
-class NonCompliantResourceTest(unittest.TestCase):
- def test_scenario_1_basic_support_non_compliant(self):
- SUPPORT_CLIENT_MOCK.describe_severity_levels = MagicMock(
- side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'SubscriptionRequiredException', 'Message': 'unknown-message'}},
- 'operation'
- )
- )
- lambda_result = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- assert_successful_evaluation(
- self,
- lambda_result,
- [build_expected_response(
- 'NON_COMPLIANT',
- '123456789012',
- annotation='The AWS Enterprise Support Plan is not enabled for this AWS Account.'
- )]
- )
- def test_scenario_2_bussiness_support_non_compliant(self):
- SUPPORT_CLIENT_MOCK.describe_severity_levels = MagicMock(
- return_value={'severityLevels': [{'code': 'urgent', 'name': 'Urgent'}]}
- )
- lambda_result = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- assert_successful_evaluation(
- self,
- lambda_result,
- [build_expected_response(
- 'NON_COMPLIANT',
- '123456789012',
- annotation='The AWS Enterprise Support Plan is not enabled for this AWS Account.'
- )]
- )
-class CompliantResourceTest(unittest.TestCase):
- def test_scenario_3_enterprice_support_compliant(self):
- SUPPORT_CLIENT_MOCK.describe_severity_levels = MagicMock(
- return_value={'severityLevels': [{'code': 'critical', 'name': 'Critical'}]}
- )
- lambda_result = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- assert_successful_evaluation(
- self,
- lambda_result,
- [build_expected_response(
- 'COMPLIANT',
- '123456789012'
- )]
- )
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ENTERPRISE_SUPPORT_PLAN_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/ENTERPRISE_SUPPORT_PLAN_ENABLED/parameters.json
deleted file mode 100644
index 1dc512d..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ENTERPRISE_SUPPORT_PLAN_ENABLED/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "ENTERPRISE_SUPPORT_PLAN_ENABLED",
- "SourceRuntime": "python3.6",
- "CodeKey": "ENTERPRISE_SUPPORT_PLAN_ENABLED.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourcePeriodic": "TwentyFour_Hours"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/GUARDDUTY_ENABLED_CENTRALIZED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/GUARDDUTY_ENABLED_CENTRALIZED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/GUARDDUTY_ENABLED_CENTRALIZED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/GUARDDUTY_ENABLED_CENTRALIZED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/GUARDDUTY_ENABLED_CENTRALIZED/parameters.json
deleted file mode 100644
index 377756a..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/GUARDDUTY_ENABLED_CENTRALIZED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "GUARDDUTY_ENABLED_CENTRALIZED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{\"CentralMonitoringAccount\": \"\"}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "GUARDDUTY_ENABLED_CENTRALIZED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/GUARDDUTY_UNTREATED_FINDINGS/GUARDDUTY_UNTREATED_FINDINGS.py b/vendor/github.com/awslabs/aws-config-rules/python/GUARDDUTY_UNTREATED_FINDINGS/GUARDDUTY_UNTREATED_FINDINGS.py
deleted file mode 100644
index c9753dd..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/GUARDDUTY_UNTREATED_FINDINGS/GUARDDUTY_UNTREATED_FINDINGS.py
+++ /dev/null
@@ -1,509 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-"""
-#####################################
-## Gherkin ##
-#####################################
-Rule Name:
- GUARDDUTY_UNTREATED_FINDINGS
-
-Description:
- Checks whether the GuardDuty has untreated findings. The rule is NON_COMPLIANT if the GuardDuty has untreated finding older than X days.
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::::Account
-
-Rule Parameters:
- daysLowSev (Optional)
- The number of days the GuardDurty Low severity findings are allowed to stay untreated (default: 30 days)
-
- daysMediumSev (Optional)
- The number of days the GuardDurty Medium severity findings are allowed to stay untreated (default: 7 days)
-
- daysHighSev (Optional)
- The number of days the GuardDurty High severity findings are allowed to stay untreated (default: 1 day)
-
-Scenarios:
- Scenario: 1
- Given: GuardDuty is Disabled or Suspended.
- Then: Return NOT_APPLICABLE
-
- Scenario: 2
- Given: The rule parameters daysLowSev/daysMediumSev/daysHighSev has invalid value.
- Then: set Default Values and evaluate
-
- Scenario: 3
- Given: No untreated GuardDuty findings are present.
- Then: Return COMPLIANT
-
- Scenario: 4
- Given: GuardDuty has untreated Low/Medium/High Severity findings.
- And: Low/Medium/High Severity findings are older than daysLowSev/daysMediumSev/daysHighSev number of days respectively.
- Then: Return NON_COMPLIANT
-
- Scenario: 5
- Given: GuardDuty has untreated Low/Medium/High Severity findings.
- And: Low/Medium/High Severity findings are not older than daysLowSev/daysMediumSev/daysHighSev number of days respectively.
- Then: Return COMPLIANT
-"""
-
-import json
-import sys
-import datetime
-from datetime import datetime
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- evaluations = []
- guardduty_client = get_client('guardduty', event)
-
- #check whether GuardDutry is enabled or Not.
- guardduty_detector_list = guardduty_client.list_detectors()
- if not guardduty_detector_list['DetectorIds']:
- return None
-
- # Guard Duty supports only one detector per region, hence retrieving first Id.
- guardduty_detector_id = guardduty_detector_list['DetectorIds'][0]
-
- # check whether GuardDuty Suspended or Not. if detector is disabled then return NOT_APPLICABLE
- guardduty_detector_status = guardduty_client.get_detector(
- DetectorId=guardduty_detector_id
- )
- if guardduty_detector_status['Status'] == 'DISABLED':
- return None
-
- # get all the guardduty findings
- all_findings_list = list_all_findings(guardduty_client, guardduty_detector_id)
-
- # If no findings present then return COMPLIANT
- if not all_findings_list:
- return build_evaluation(event['accountId'], 'COMPLIANT', event)
-
- # evaluate each finding
- for each_finding in all_findings_list:
- days_since_created = get_delta_days(each_finding['CreatedAt'])
-
- # 0.1 to 3.9 severity score is LowSev
- if 0.1 <= each_finding['Severity'] <= 3.9:
- if days_since_created <= valid_rule_parameters['daysLowSev']:
- evaluations.append(build_evaluation(each_finding['Id'], 'COMPLIANT', event))
- continue
- evaluations.append(build_evaluation(each_finding['Id'], 'NON_COMPLIANT', event, annotation='This AWS GurdDuty Low Severity finding is older than {} days.'.format(valid_rule_parameters['daysLowSev'])))
- continue
-
- # 4.0 to 6.9 severity score is MediumSev
- if 4.0 <= each_finding['Severity'] <= 6.9:
- if days_since_created <= valid_rule_parameters['daysMediumSev']:
- evaluations.append(build_evaluation(each_finding['Id'], 'COMPLIANT', event))
- continue
- evaluations.append(build_evaluation(each_finding['Id'], 'NON_COMPLIANT', event, annotation='This AWS GurdDuty Medium Severity finding is older than {} days.'.format(valid_rule_parameters['daysMediumSev'])))
- continue
-
- # 7.0 to 8.9 severity score is HighSev
- if 7.0 <= each_finding['Severity'] <= 8.9:
- if days_since_created <= valid_rule_parameters['daysHighSev']:
- evaluations.append(build_evaluation(each_finding['Id'], 'COMPLIANT', event))
- continue
- evaluations.append(build_evaluation(each_finding['Id'], 'NON_COMPLIANT', event, annotation='This AWS GurdDuty High Severity finding is older than {} days.'.format(valid_rule_parameters['daysHighSev'])))
- continue
-
- return evaluations
-
-
-# get number of days since the finding is created
-def get_delta_days(finding_creation_time):
- current_datetime_object = datetime.utcnow()
- creation_datetime_object = datetime.strptime(finding_creation_time, '%Y-%m-%dT%H:%M:%S.%fZ')
- delta_time = current_datetime_object - creation_datetime_object
- return delta_time.days
-
-# function to get all findings. Each call supports maximum of 50 items.
-def list_all_findings(guardduty_client, guardduty_detector_id):
- all_findings_list = []
- findings_id_list = guardduty_client.list_findings(DetectorId=guardduty_detector_id)
- if not findings_id_list['FindingIds']:
- return all_findings_list
-
- findings_list = guardduty_client.get_findings(DetectorId=guardduty_detector_id, FindingIds=findings_id_list['FindingIds'])
- all_findings_list += findings_list['Findings']
- while True:
- if findings_id_list['NextToken']:
- findings_id_list = guardduty_client.list_findings(DetectorId=guardduty_detector_id, NextToken=findings_id_list['NextToken'])
- findings_list = guardduty_client.get_findings(DetectorId=guardduty_detector_id, FindingIds=findings_id_list['FindingIds'])
- all_findings_list += findings_list['Findings']
- else:
- break
-
- return all_findings_list
-
-
-def evaluate_parameters(rule_parameters):
- default_days_low_sev = 30
- default_days_medium_sev = 7
- default_days_high_sev = 1
-
- if "daysLowSev" not in rule_parameters:
- rule_parameters['daysLowSev'] = default_days_low_sev
- elif not validate_parameter_value(rule_parameters['daysLowSev']):
- raise ValueError('Invalid value for parameter \'daysLowSev\', Expected Number of Days in digits.')
-
- if "daysMediumSev" not in rule_parameters:
- rule_parameters['daysMediumSev'] = default_days_medium_sev
- elif not validate_parameter_value(rule_parameters['daysMediumSev']):
- raise ValueError('Invalid value for parameter \'daysMediumSev\', Expected Number of Days in digits.')
-
- if "daysHighSev" not in rule_parameters:
- rule_parameters['daysHighSev'] = default_days_high_sev
- elif not validate_parameter_value(rule_parameters['daysHighSev']):
- raise ValueError('Invalid value for parameter \'daysHighSev\', Expected Number of Days in digits.')
-
- #change string values to int
- rule_parameters['daysLowSev'] = int(float(rule_parameters['daysLowSev']))
- rule_parameters['daysMediumSev'] = int(float(rule_parameters['daysMediumSev']))
- rule_parameters['daysHighSev'] = int(float(rule_parameters['daysHighSev']))
-
- return rule_parameters
-
-# returns False if the given value is not a Positive Number.
-def validate_parameter_value(parameter_value):
- try:
- float(parameter_value)
- except:
- return False
- if float(parameter_value) < 0:
- return False
-
- return True
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return status in ('OK', 'ResourceDiscovered') and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- # print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/GUARDDUTY_UNTREATED_FINDINGS/GUARDDUTY_UNTREATED_FINDINGS_test.py b/vendor/github.com/awslabs/aws-config-rules/python/GUARDDUTY_UNTREATED_FINDINGS/GUARDDUTY_UNTREATED_FINDINGS_test.py
deleted file mode 100644
index 17c6a74..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/GUARDDUTY_UNTREATED_FINDINGS/GUARDDUTY_UNTREATED_FINDINGS_test.py
+++ /dev/null
@@ -1,355 +0,0 @@
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-from datetime import datetime
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-GUARDDUTY_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 'guardduty':
- return GUARDDUTY_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('GUARDDUTY_UNTREATED_FINDINGS')
-
-class ComplianceTestCases(unittest.TestCase):
-
- rule_valid_parameters = '{"daysLowSev":"20", "daysMediumSev":"10", "daysHighSev":"3"}'
- rule_invalid_parameters = '{"daysLowSev":"Twenty", "daysMediumSev":"-10.0", "daysHighSev":"None"}'
-
- gd_detector_list_enabled = {
- "DetectorIds": [
- "fab515984fc563fa95f7ab82c5dd69c7"
- ],
- }
-
- gd_detector_list_disabled = {
- "DetectorIds": [
- ],
- }
-
- gd_detector_status_active = {
- "Status": "ENABLED",
- "FindingPublishingFrequency": "SIX_HOURS",
- "CreatedAt": "2019-04-17T12:06:08.108Z"
- }
-
- gd_detector_status_suspended = {
- "Status": "DISABLED",
- "FindingPublishingFrequency": "SIX_HOURS",
- "CreatedAt": "2019-04-17T12:06:08.108Z"
- }
-
- gd_empty_findings_list = {
- "FindingIds": [
- ],
- "NextToken": ""
- }
-
- gd_findings_list = {
- "FindingIds": [
- "42b5159faf35b2b33df670ac2aa4b943"
- ],
- "NextToken": ""
- }
-
- gd_finding_lowSev_compliant = {
- "Findings": [
- {
- "Severity": 2,
- "Id": "42b5159faf35b2b33df670ac2aa4b943",
- "CreatedAt": str(datetime.utcnow()).replace(" ", "T")[:-3] + "Z",
- }
- ]
- }
-
- gd_finding_MediumSev_compliant = {
- "Findings": [
- {
- "Severity": 5,
- "Id": "42b5159faf35b2b33df670ac2aa4b943",
- "CreatedAt": str(datetime.utcnow()).replace(" ", "T")[:-3] + "Z",
- }
- ]
- }
-
- gd_finding_highSev_compliant = {
- "Findings": [
- {
- "Severity": 8,
- "Id": "42b5159faf35b2b33df670ac2aa4b943",
- "CreatedAt": str(datetime.utcnow()).replace(" ", "T")[:-3] + "Z",
- }
- ]
- }
-
- gd_finding_lowSev_noncompliant = {
- "Findings": [
- {
- "Severity": 2,
- "Id": "42b5159faf35b2b33df670ac2aa4b943",
- "CreatedAt": "2019-03-10T10:33:57.404Z",
- }
- ]
- }
-
- gd_finding_MedSev_noncompliant = {
- "Findings": [
- {
- "Severity": 5,
- "Id": "42b5159faf35b2b33df670ac2aa4b943",
- "CreatedAt": "2019-04-10T10:33:57.404Z",
- }
- ]
- }
-
- gd_highSev_noncompliant = {
- "Findings": [
- {
- "Severity": 8,
- "Id": "42b5159faf35b2b33df670ac2aa4b943",
- "CreatedAt": "2019-04-24T10:33:57.404Z",
- }
- ]
- }
-
- def setUp(self):
- pass
-
- # Common test scenario 1
- def test_guardduty_disabled(self):
- RULE.ASSUME_ROLE_MODE = False
- GUARDDUTY_CLIENT_MOCK.list_detectors = MagicMock(return_value=self.gd_detector_list_disabled)
- response = RULE.lambda_handler(build_lambda_scheduled_event(self.rule_valid_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- # Common test scenario 2
- def test_guardduty_suspended(self):
- RULE.ASSUME_ROLE_MODE = False
- GUARDDUTY_CLIENT_MOCK.list_detectors = MagicMock(return_value=self.gd_detector_list_disabled)
- GUARDDUTY_CLIENT_MOCK.get_detector = MagicMock(return_value=self.gd_detector_status_suspended)
- response = RULE.lambda_handler(build_lambda_scheduled_event(self.rule_valid_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- # Common test scenario 3
- def test_guardduty_invalid_parameters(self):
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_scheduled_event(self.rule_invalid_parameters), {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException', 'Invalid value for parameter \'daysLowSev\', Expected Number of Days in digits.')
-
- # Scenario 3
- def test_guardduty_no_findings(self):
- RULE.ASSUME_ROLE_MODE = False
- GUARDDUTY_CLIENT_MOCK.list_detectors = MagicMock(return_value=self.gd_detector_list_enabled)
- GUARDDUTY_CLIENT_MOCK.get_detector = MagicMock(return_value=self.gd_detector_status_active)
- GUARDDUTY_CLIENT_MOCK.list_findings = MagicMock(return_value=self.gd_empty_findings_list)
- response = RULE.lambda_handler(build_lambda_scheduled_event(self.rule_valid_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- # Scenario 4a
- def test_guardduty_lowsev_non_compliant(self):
- RULE.ASSUME_ROLE_MODE = False
- GUARDDUTY_CLIENT_MOCK.list_detectors = MagicMock(return_value=self.gd_detector_list_enabled)
- GUARDDUTY_CLIENT_MOCK.get_detector = MagicMock(return_value=self.gd_detector_status_active)
- GUARDDUTY_CLIENT_MOCK.list_findings = MagicMock(return_value=self.gd_findings_list)
- GUARDDUTY_CLIENT_MOCK.get_findings = MagicMock(return_value=self.gd_finding_lowSev_noncompliant)
- response = RULE.lambda_handler(build_lambda_scheduled_event(self.rule_valid_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '42b5159faf35b2b33df670ac2aa4b943', 'AWS::::Account', 'This AWS GurdDuty Low Severity finding is older than 20 days.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- # Scenario 4b
- def test_guardduty_mediumsev_non_compliant(self):
- RULE.ASSUME_ROLE_MODE = False
- GUARDDUTY_CLIENT_MOCK.list_detectors = MagicMock(return_value=self.gd_detector_list_enabled)
- GUARDDUTY_CLIENT_MOCK.get_detector = MagicMock(return_value=self.gd_detector_status_active)
- GUARDDUTY_CLIENT_MOCK.list_findings = MagicMock(return_value=self.gd_findings_list)
- GUARDDUTY_CLIENT_MOCK.get_findings = MagicMock(return_value=self.gd_finding_MedSev_noncompliant)
- response = RULE.lambda_handler(build_lambda_scheduled_event(self.rule_valid_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '42b5159faf35b2b33df670ac2aa4b943', 'AWS::::Account', 'This AWS GurdDuty Medium Severity finding is older than 10 days.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- # Scenario 4c
- def test_guardduty_highsev_non_compliant(self):
- RULE.ASSUME_ROLE_MODE = False
- GUARDDUTY_CLIENT_MOCK.list_detectors = MagicMock(return_value=self.gd_detector_list_enabled)
- GUARDDUTY_CLIENT_MOCK.get_detector = MagicMock(return_value=self.gd_detector_status_active)
- GUARDDUTY_CLIENT_MOCK.list_findings = MagicMock(return_value=self.gd_findings_list)
- GUARDDUTY_CLIENT_MOCK.get_findings = MagicMock(return_value=self.gd_highSev_noncompliant)
- response = RULE.lambda_handler(build_lambda_scheduled_event(self.rule_valid_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '42b5159faf35b2b33df670ac2aa4b943', 'AWS::::Account', 'This AWS GurdDuty High Severity finding is older than 3 days.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- # Scenario 5a
- def test_guardduty_lowsev_compliant(self):
- RULE.ASSUME_ROLE_MODE = False
- GUARDDUTY_CLIENT_MOCK.list_detectors = MagicMock(return_value=self.gd_detector_list_enabled)
- GUARDDUTY_CLIENT_MOCK.get_detector = MagicMock(return_value=self.gd_detector_status_active)
- GUARDDUTY_CLIENT_MOCK.list_findings = MagicMock(return_value=self.gd_findings_list)
- GUARDDUTY_CLIENT_MOCK.get_findings = MagicMock(return_value=self.gd_finding_lowSev_compliant)
- response = RULE.lambda_handler(build_lambda_scheduled_event(self.rule_valid_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', '42b5159faf35b2b33df670ac2aa4b943', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- # Scenario 5b
- def test_guardduty_mediumsev_compliant(self):
- RULE.ASSUME_ROLE_MODE = False
- GUARDDUTY_CLIENT_MOCK.list_detectors = MagicMock(return_value=self.gd_detector_list_enabled)
- GUARDDUTY_CLIENT_MOCK.get_detector = MagicMock(return_value=self.gd_detector_status_active)
- GUARDDUTY_CLIENT_MOCK.list_findings = MagicMock(return_value=self.gd_findings_list)
- GUARDDUTY_CLIENT_MOCK.get_findings = MagicMock(return_value=self.gd_finding_MediumSev_compliant)
- response = RULE.lambda_handler(build_lambda_scheduled_event(self.rule_valid_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', '42b5159faf35b2b33df670ac2aa4b943', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- # Scenario 5c
- def test_guardduty_highsev_compliant(self):
- RULE.ASSUME_ROLE_MODE = False
- GUARDDUTY_CLIENT_MOCK.list_detectors = MagicMock(return_value=self.gd_detector_list_enabled)
- GUARDDUTY_CLIENT_MOCK.get_detector = MagicMock(return_value=self.gd_detector_status_active)
- GUARDDUTY_CLIENT_MOCK.list_findings = MagicMock(return_value=self.gd_findings_list)
- GUARDDUTY_CLIENT_MOCK.get_findings = MagicMock(return_value=self.gd_finding_highSev_compliant)
- response = RULE.lambda_handler(build_lambda_scheduled_event(self.rule_valid_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', '42b5159faf35b2b33df670ac2aa4b943', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/GUARDDUTY_UNTREATED_FINDINGS/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/GUARDDUTY_UNTREATED_FINDINGS/parameters.json
deleted file mode 100644
index a6c73ca..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/GUARDDUTY_UNTREATED_FINDINGS/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "GUARDDUTY_UNTREATED_FINDINGS",
- "SourceRuntime": "python3.6",
- "CodeKey": "GUARDDUTY_UNTREATED_FINDINGS.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{\"daysLowSev\": \"30\", \"daysMediumSev\": \"7\", \"daysHighSev\": \"1\"}",
- "SourcePeriodic": "One_Hour"
- },
- "Tags": "[]"
-}
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_ACCESS_KEY_ROTATED/IAM_ACCESS_KEY_ROTATED.py b/vendor/github.com/awslabs/aws-config-rules/python/IAM_ACCESS_KEY_ROTATED/IAM_ACCESS_KEY_ROTATED.py
deleted file mode 100644
index 956c613..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_ACCESS_KEY_ROTATED/IAM_ACCESS_KEY_ROTATED.py
+++ /dev/null
@@ -1,507 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-'''
-#####################################
-## Gherkin ##
-#####################################
-
-
- Rule Name:
- IAM_ACCESS_KEY_ROTATED
-
- Description:
- To check IAM users access key are rotated by a specific number of days (by default 90 days).
-
- Trigger:
- Periodic
-
- Reports on:
- AWS::IAM::User
-
- Rule Parameters:
- | ---------------------- | --------- | -------------------------------------------------------- |
- | Parameter Name | Type | Description |
- | ---------------------- | --------- | -------------------------------------------------------- |
- | WhitelistedUserList | Optional | Represents the IAM users which are exempted from the IAM |
- | | | Config rule. The valid users in this list will be |
- | | | compliant by default. |
- | | | List of all the UniqueID (user ID) separated by a comma. |
- | ---------------------- | --------- | -------------------------------------------------------- |
- | KeyKeyActiveTimeOutInDays | Optional | Numeric variable representing the maximum age in days of |
- | | | access key. The default value is 90. |
- | ---------------------- | --------- | -------------------------------------------------------- |
-
-
- Feature:
- In order to: enforce security best practices for IAM users
- As: a Security Officer
- I want: To ensure that IAM users are never left active, except if whitelisted
-
- Scenarios:
-
- Scenario: 1
- Given: No IAM Users
- Then: Return "NOT_APPLICABLE"
-
- Scenario: 2
- Given: WhitelistedUserList is configured
- And: A user listed in WhitelistedUserList is not an alphanumerical string starting with "AIDA"
- Then: Return an error
-
- Scenario: 3
- Given: KeyKeyActiveTimeOutInDays is configured
- And: KeyKeyActiveTimeOutInDays is not a positive integer smaller than 999999999
- Then: Return an error
-
- Scenario: 4
- Given: An IAM user
- And: WhitelistedUserList is configured and valid
- And: The IAM user is listed on the WhitelistedUserList
- Then: Return COMPLIANT
-
- Scenario: 5
- Given: An IAM user
- And: The IAM User has no access keys
- Then: Return COMPLIANT
-
- Scenario: 6
- Given: An IAM user
- And: The IAM User has no active access keys
- Then: Return COMPLIANT
-
- Scenario: 7
- Given: An IAM user
- And: The IAM user is not listed on the WhitelistedUserList, if configured
- And: The IAM user has one or more active access keys
- And: The oldest key age is more or equal than KeyKeyActiveTimeOutInDays, if configured (or default, if not configured)
- Then: return NON_COMPLIANT
-
- Scenario: 8
- Given: An IAM user
- And: The IAM user is not listed on the WhitelistedUserList, if configured
- And: The IAM user has one or more active access keys
- And: The oldest key age is less than KeyKeyActiveTimeOutInDays, if configured (or default, if not configured)
- Then: return COMPLIANT
-'''
-
-import json
-from datetime import datetime, timedelta
-import dateutil.parser
-import re
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::IAM::User'
-DEFAULT_NUMBER_OF_DAYS = 90
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
-
- iam_client = get_client('iam', event)
- evaluations = []
-
- users_list = get_all_users(iam_client)
-
- if not users_list:
- return None
-
- for user in users_list:
- if user['UserId'] in valid_rule_parameters['WhitelistedUserList']:
- evaluations.append(build_evaluation(user['UserId'], 'COMPLIANT', event, annotation='This user ({}) is whitelisted.'.format(user['UserId'])))
- continue
- keys_list = iam_client.list_access_keys(UserName=user['UserName'])
- expired_key = False
- for key in keys_list['AccessKeyMetadata']:
- if key['Status'] == 'Inactive':
- continue
- if not is_key_still_valid(key['CreateDate'], valid_rule_parameters['KeyActiveTimeOutInDays']):
- expired_key = True
- evaluations.append(build_evaluation(user['UserId'], 'NON_COMPLIANT', event, annotation='This user ({}) has an expired active access key ({}). The key is older than {}. It must be no older than {} days.'.format(user['UserId'], key['AccessKeyId'], str(key_age(key['CreateDate'])).split(',')[0], valid_rule_parameters['KeyActiveTimeOutInDays'])))
- break
- if not expired_key:
- evaluations.append(build_evaluation(user['UserId'], 'COMPLIANT', event))
-
- return evaluations
-
-def get_all_users(client):
- list_to_return = []
- user_list = client.list_users()
- while True:
- for user in user_list['Users']:
- list_to_return.append(user)
- if 'Marker' in user_list:
- user_list = client.list_users(Marker=user_list['Marker'])
- else:
- break
- return list_to_return
-
-def is_key_still_valid(create_date, timeout_days):
- expiry_time = timedelta(days=timeout_days)
- if expiry_time > key_age(create_date):
- return True
- return False
-
-def key_age(create_date):
- today = datetime.utcnow().replace(tzinfo=dateutil.tz.tzutc())
- time_delta = today - create_date
- return time_delta
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
-
- valid_rule_parameters = {}
-
- valid_rule_parameters['WhitelistedUserList'] = []
- if 'WhitelistedUserList' in rule_parameters:
- whitelisted_users = rule_parameters['WhitelistedUserList'].replace(' ', '').split(',')
- valid_whitelist = []
- for whitelisted_user in whitelisted_users:
- if not re.match("^AIDA[A-Z0-9]+$", whitelisted_user):
- raise ValueError('Invalid Parameter WhitelistedUser: ' + whitelisted_user)
- valid_whitelist.append(whitelisted_user)
- valid_rule_parameters['WhitelistedUserList'] = valid_whitelist
-
- valid_rule_parameters['KeyActiveTimeOutInDays'] = DEFAULT_NUMBER_OF_DAYS
- if 'KeyActiveTimeOutInDays' in rule_parameters:
- try:
- valid_rule_parameters['KeyActiveTimeOutInDays'] = int(str(rule_parameters['KeyActiveTimeOutInDays']))
- except:
- raise ValueError("Invalid Parameter KeyActiveTimeOutInDays: not an integer.")
-
- if valid_rule_parameters['KeyActiveTimeOutInDays'] < 0:
- raise ValueError("Invalid Parameter KeyActiveTimeOutInDays: use a positive value.")
-
- if valid_rule_parameters['KeyActiveTimeOutInDays'] > 999999999:
- raise ValueError("Invalid Parameter KeyActiveTimeOutInDays: use a value less than 999999999.")
-
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Parameter value is invalid",
- internalErrorDetails="An ValueError was raised during the validation of the Parameter value",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while(evaluation_copy):
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=resultToken, TestMode=testMode)
- del evaluation_copy[:100]
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_ACCESS_KEY_ROTATED/IAM_ACCESS_KEY_ROTATED_test.py b/vendor/github.com/awslabs/aws-config-rules/python/IAM_ACCESS_KEY_ROTATED/IAM_ACCESS_KEY_ROTATED_test.py
deleted file mode 100644
index ff2d67b..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_ACCESS_KEY_ROTATED/IAM_ACCESS_KEY_ROTATED_test.py
+++ /dev/null
@@ -1,258 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-import json
-from datetime import datetime, timedelta
-import dateutil.parser
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::IAM::User'
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-iam_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'iam':
- return iam_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('IAM_ACCESS_KEY_ROTATED')
-
-class test_invalid_parameters(unittest.TestCase):
- def setUp(self):
- config_client_mock.reset_mock()
- iam_client_mock.reset_mock()
-
- invalid_user_whiteList_params = {
- "invalidEntry" : ['{"WhitelistedUserList":"12345"}',
- '{"WhitelistedUserList":"ABCDEF12356"}',
- '{"WhitelistedUserList":"mbjfEF12356"}',
- '{"WhitelistedUserList":"AIDA"}',
- '{"WhitelistedUserList":"AIDA*&903"}',
- '{"WhitelistedUserList":"AIDA325ykvo"}',
- '{"WhitelistedUserList":"(%$@!)"}'],
- "invalidSeparators" : ['{"WhitelistedUserList":"AIDAJYPPIFB65RVYU7CCW/AIDAJYPPIFB65RVY9IP62"}',
- '{"WhitelistedUserList":"AIDAJYPPIFB65RVYU7CCW,,AIDAJYPPILP90RVYU7WWC"}']
- }
-
- invalid_expiry_params = ['{"KeyActiveTimeOutInDays":"-1"}',
- '{"KeyActiveTimeOutInDays":"9999999999"}',
- '{"KeyActiveTimeOutInDays":"5.6"}',
- '{"KeyActiveTimeOutInDays":"ABC"}',
- '{"KeyActiveTimeOutInDays":"*&^"}']
-
- def test_scenario2_user_whitelist_parameters_incorrect_entry(self):
- for invalid_param in self.invalid_user_whiteList_params['invalidEntry']:
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=invalid_param), {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
- def test_scenario2_user_whitelist_parameters_incorrect_separators(self):
- for invalid_param in self.invalid_user_whiteList_params['invalidSeparators']:
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=invalid_param), {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
- def test_scenario3_KeyActiveTimeOutInDays_parameters_incorrect_entry(self):
- for invalid_param in self.invalid_expiry_params:
- response = {}
- response = rule.lambda_handler(build_lambda_scheduled_event(rule_parameters=invalid_param), {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
-class test_compliance(unittest.TestCase):
- def setUp(self):
- config_client_mock.reset_mock()
- iam_client_mock.reset_mock()
-
- user_list_empty = {"Users" : []}
- user_list_whitelist = {"Users": [{'UserId': 'AIDAJYPPIFB65RV8YYLDU','UserName': 'sampleUser1'}]}
- user_list = {"Users": [{'UserId': 'AIDAJYPPIFB65RV8YYLDU','UserName': 'sampleUser1'}, {'UserId': 'AIDAJYPPIFB65RV8YYLDV','UserName': 'sampleUser2'}]}
-
- no_access_key = {'AccessKeyMetadata': []}
- no_access_key_active = {'AccessKeyMetadata': [{'AccessKeyId': 'AKIAIPPNIMKJA2N7SJRA', 'Status': 'Inactive', 'UserName': 'sampleUser'}]}
-
- def construct_list_access_keys_response(self, age_key):
- list_access_keys_call = {}
- list_access_keys_call = {'AccessKeyMetadata': [{'AccessKeyId': 'AKIAIPPNIMKJA2N7SJRA',
- 'CreateDate': datetime.utcnow().replace(tzinfo=dateutil.tz.tzutc()) - timedelta(days=age_key),
- 'Status': 'Active', 'UserName': 'sampleUser'}]}
- return list_access_keys_call
-
- def test_scenario1_no_iam_users(self):
- iam_client_mock.list_users = MagicMock(return_value=self.user_list_empty)
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response("NOT_APPLICABLE", "123456789012", compliance_resource_type="AWS::::Account"))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario4_compliant_user_whitelist(self):
- iam_client_mock.list_users = MagicMock(return_value=self.user_list)
- response = rule.lambda_handler(build_lambda_scheduled_event('{"WhitelistedUserList":"AIDAJYPPIFB65RV8YYLDU"}'), {})
- resp_expected = []
- resp_expected.append(build_expected_response("COMPLIANT", "AIDAJYPPIFB65RV8YYLDU", annotation='This user (AIDAJYPPIFB65RV8YYLDU) is whitelisted.'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- def test_scenario5_noncompliant_users_no_access_key(self):
- iam_client_mock.list_users = MagicMock(return_value=self.user_list)
- iam_client_mock.list_access_keys = MagicMock(return_value=self.no_access_key)
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response("COMPLIANT", "AIDAJYPPIFB65RV8YYLDU"))
- resp_expected.append(build_expected_response("COMPLIANT", "AIDAJYPPIFB65RV8YYLDV"))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- def test_scenario6_noncompliant_users_no_active_access_key(self):
- iam_client_mock.list_users = MagicMock(return_value=self.user_list)
- iam_client_mock.list_access_keys = MagicMock(return_value=self.no_access_key_active)
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response("COMPLIANT", "AIDAJYPPIFB65RV8YYLDU"))
- resp_expected.append(build_expected_response("COMPLIANT", "AIDAJYPPIFB65RV8YYLDV"))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- def test_scenario7_noncompliant_users_custom_timeout(self):
- custom_timeout = 20
- list_access_keys_response = self.construct_list_access_keys_response(90)
- iam_client_mock.list_users = MagicMock(return_value=self.user_list)
- iam_client_mock.list_access_keys = MagicMock(return_value = list_access_keys_response)
- response = rule.lambda_handler(build_lambda_scheduled_event('{"KeyActiveTimeOutInDays":' + str(custom_timeout) + '}'), {})
- resp_expected = []
- resp_expected.append(build_expected_response("NON_COMPLIANT", "AIDAJYPPIFB65RV8YYLDU", annotation='This user (AIDAJYPPIFB65RV8YYLDU) has an expired active access key (AKIAIPPNIMKJA2N7SJRA). The key is older than 90 days. It must be no older than 20 days.'))
- resp_expected.append(build_expected_response("NON_COMPLIANT", "AIDAJYPPIFB65RV8YYLDV", annotation='This user (AIDAJYPPIFB65RV8YYLDV) has an expired active access key (AKIAIPPNIMKJA2N7SJRA). The key is older than 90 days. It must be no older than 20 days.'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- def test_scenario7_noncompliant_users_default_timeout(self):
- list_access_keys_response = self.construct_list_access_keys_response(120)
- iam_client_mock.list_users = MagicMock(return_value=self.user_list)
- iam_client_mock.list_access_keys = MagicMock(return_value = list_access_keys_response)
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response("NON_COMPLIANT", "AIDAJYPPIFB65RV8YYLDU", annotation='This user (AIDAJYPPIFB65RV8YYLDU) has an expired active access key (AKIAIPPNIMKJA2N7SJRA). The key is older than 120 days. It must be no older than 90 days.'))
- resp_expected.append(build_expected_response("NON_COMPLIANT", "AIDAJYPPIFB65RV8YYLDV", annotation='This user (AIDAJYPPIFB65RV8YYLDV) has an expired active access key (AKIAIPPNIMKJA2N7SJRA). The key is older than 120 days. It must be no older than 90 days.'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- def test_scenario8_compliant_users_default_timeout(self):
- list_access_keys_response = self.construct_list_access_keys_response(80)
- iam_client_mock.list_users = MagicMock(return_value=self.user_list)
- iam_client_mock.list_access_keys = MagicMock(return_value = list_access_keys_response)
- response = rule.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response("COMPLIANT", "AIDAJYPPIFB65RV8YYLDU"))
- resp_expected.append(build_expected_response("COMPLIANT", "AIDAJYPPIFB65RV8YYLDV"))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- def test_scenario8_compliant_users_custom_timeout(self):
- list_access_keys_response = self.construct_list_access_keys_response(10)
- custom_timeout = 20
- iam_client_mock.list_users = MagicMock(return_value=self.user_list)
- iam_client_mock.list_access_keys = MagicMock(return_value = list_access_keys_response)
- response = rule.lambda_handler(build_lambda_scheduled_event('{"KeyActiveTimeOutInDays":' + str(custom_timeout) + '}'), {})
- resp_expected = []
- resp_expected.append(build_expected_response("COMPLIANT", "AIDAJYPPIFB65RV8YYLDU"))
- resp_expected.append(build_expected_response("COMPLIANT", "AIDAJYPPIFB65RV8YYLDV"))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- rule_parameters = json.dumps(rule_parameters)
- invoking_event = json.dumps(invoking_event)
-
- invoking_event = '{"awsAccountId":"825908965814","notificationCreationTime":"2018-03-01T11:21:06.236Z","messageType":"ScheduledNotification","recordVersion":"1.0"}'
-
- event_to_return = {'accountId': 'AIDAJYPPIFB65RV8YYLDU',
- 'configRuleArn': 'arn:aws:config:ap-south-1:825908965814:config-rule/config-rule-swb7as',
- 'configRuleId': 'config-rule-swb7as',
- 'configRuleName': 'iam-principal-used-90-days',
- 'eventLeftScope': False,
- 'executionRoleArn': 'arn:aws:iam::825908965814:role/service-role/config-role-ap-south-1',
- 'invokingEvent': invoking_event,
- 'resultToken': 'TESTMODE',
- 'valid_rule_parameters': rule_parameters,
- 'version': '1.0'}
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(testClass, response, customerErrorCode=None, customerErrorMessage=None):
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_ACCESS_KEY_ROTATED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/IAM_ACCESS_KEY_ROTATED/parameters.json
deleted file mode 100644
index 3afd4f7..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_ACCESS_KEY_ROTATED/parameters.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "IAM_ACCESS_KEY_ROTATED",
- "SourceRuntime": "python3.6",
- "CodeKey": "IAM_ACCESS_KEY_ROTATED.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{\"WhitelistedUserList\":\"\",\"KeyActiveTimeOutInDays\":\"\"}",
- "SourcePeriodic": "TwentyFour_Hours",
- "RuleSets": [
- "baseline",
- "rulecriticity:high",
- "pci",
- "pci:8.1",
- "iam"
- ]
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_GROUP_NO_POLICY_FULL_STAR/IAM_GROUP_NO_POLICY_FULL_STAR.py b/vendor/github.com/awslabs/aws-config-rules/python/IAM_GROUP_NO_POLICY_FULL_STAR/IAM_GROUP_NO_POLICY_FULL_STAR.py
deleted file mode 100644
index 956ca18..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_GROUP_NO_POLICY_FULL_STAR/IAM_GROUP_NO_POLICY_FULL_STAR.py
+++ /dev/null
@@ -1,460 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-'''
-#####################################
-## Gherkin ##
-#####################################
-
- Rule Name:
- iam-group-no-policy-full-star
-
- Description:
- Check whether IAM Groups have an IAM policy with all permission enabled (or "Action:*"). IAM Policy includes the AWS-managed policy named AdministratorAccess, Customer-managed policies and inline policies.
-
- Trigger:
- Configuration Change on AWS::IAM::Group
-
- Reports on:
- AWS::IAM::Group
-
- Rule Parameters:
- None
-
- Feature:
- In order to: ensure that policies attached to any group does not have full administrative privileges
- As: a Security Officer
- I want: to ensure that no policy have "*" in their action.
-
- Scenarios:
- Scenario 1:
- Given: Any allow statements of group has Action as "*"
- Then: Return NON_COMPLIANT
-
- Scenario 2:
- Given: All allow statments of group do not have Action as "*"
- Then: Return COMPLIANT
-
- Scenario 3:
- Given: No policy is applying to the group
- Then: Return COMPLIANT
-
- Examples:
- | Policy |
- | inline policy |
- | aws-managed predefined policy |
- | customer-managed defined policy |
-'''
-import json
-import datetime
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::IAM::Group'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
-
- group_name = configuration_item['configuration']['groupName']
- iam_client = get_client('iam', event)
-
- # Inline policies
- inline_policy_names = get_all_group_inline_policy_names(iam_client, group_name)
- for policy_name in inline_policy_names:
- policy_document = iam_client.get_group_policy(GroupName=group_name, PolicyName=policy_name)['PolicyDocument']
- if is_statements_include_full_star_allow(policy_document['Statement']):
- return build_evaluation_from_config_item(configuration_item, "NON_COMPLIANT", annotation='An inline policy "' + policy_name + '" attached to the group "' + group_name + '" has full star allow permissions.')
-
- # Managed policies
- managed_policy_arn_and_name = get_all_group_managed_policy_arn_and_name(iam_client, group_name)
- for policy_arn, policy_name in managed_policy_arn_and_name.items():
- get_policy = iam_client.get_policy(PolicyArn=policy_arn)
- version = get_policy['Policy']['DefaultVersionId']
- get_policy_version = iam_client.get_policy_version(PolicyArn=policy_arn, VersionId=version)
- if is_statements_include_full_star_allow(get_policy_version['PolicyVersion']['Document']['Statement']):
- return build_evaluation_from_config_item(configuration_item, "NON_COMPLIANT", annotation='A managed policy with name "' + policy_name + '" attached to the group "' + group_name + '" has full star allow permissions.')
-
- return "COMPLIANT"
-
-def get_all_group_inline_policy_names(iam_client, group_name):
- all_group_inline_policies = []
- list_policy_names = iam_client.list_group_policies(GroupName=group_name, MaxItems=1000)
- while True:
- all_group_inline_policies += list_policy_names['PolicyNames']
- if 'Marker' in list_policy_names:
- list_policy_names = iam_client.list_group_policies(GroupName=group_name, MaxItems=1000, Marker=list_policy_names['Marker'])
- else:
- break
- return all_group_inline_policies
-
-def get_all_group_managed_policy_arn_and_name(iam_client, group_name):
- all_group_managed_policies_arn_and_name = {}
- list_policy_arn = iam_client.list_attached_group_policies(GroupName=group_name, MaxItems=1000)
- while True:
- for policy_dict in list_policy_arn['AttachedPolicies']:
- all_group_managed_policies_arn_and_name[policy_dict['PolicyArn']] = policy_dict['PolicyName']
- if 'Marker' in list_policy_arn:
- list_policy_arn = iam_client.list_attached_group_policies(GroupName=group_name, MaxItems=1000, Marker=list_policy_arn['Marker'])
- else:
- break
- return all_group_managed_policies_arn_and_name
-
-def is_statements_include_full_star_allow(statements):
- statement_list = []
- if isinstance(statements, dict):
- statement_list = [statements]
- elif isinstance(statements, list):
- statement_list = statements
- else:
- print("Not recognized statement type:")
- print(statements)
- return False
-
- for statement in statement_list:
- if statement['Effect'] == 'Deny':
- continue
-
- if 'Action' not in statement:
- print("No 'Action' in statement")
- print(statement)
- continue
-
- if isinstance(statement['Action'], list):
- for action in statement['Action']:
- if action == "*":
- return True
- else:
- if statement['Action'] == "*":
- return True
- return False
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Parameter value is invalid",
- internalErrorDetails="An ValueError was raised during the validation of the Parameter value",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluations, ResultToken=resultToken, TestMode=testMode)
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_GROUP_NO_POLICY_FULL_STAR/IAM_GROUP_NO_POLICY_FULL_STAR_test.py b/vendor/github.com/awslabs/aws-config-rules/python/IAM_GROUP_NO_POLICY_FULL_STAR/IAM_GROUP_NO_POLICY_FULL_STAR_test.py
deleted file mode 100644
index cf6f74f..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_GROUP_NO_POLICY_FULL_STAR/IAM_GROUP_NO_POLICY_FULL_STAR_test.py
+++ /dev/null
@@ -1,196 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::IAM::Group'
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-iam_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'sts':
- return sts_client_mock
- elif client_name == 'iam':
- return iam_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('IAM_GROUP_NO_POLICY_FULL_STAR')
-
-class ComplianceTest(unittest.TestCase):
-
- invoking_event = '{"configurationItemDiff":"SomeDifference", "notificationCreationTime":"SomeTime", "messageType":"ConfigurationItemChangeNotification", "recordVersion":"SomeVersion", "configurationItem":{ "resourceType":"AWS::IAM::Group","configurationItemStatus":"ResourceDiscovered", "resourceId":"AIDAICVB3PKAQMPEGDW2C", "configurationItemCaptureTime":"2018-02-20T06:56:55.533Z", "configuration":{"groupName": "somegroupname"}}}'
-
- list_group_policy_names = {'PolicyNames': ['policyname1', 'policyname2']}
- get_group_policy_doc = {'PolicyDocument': {"Statement": [{"Effect": "Allow", "Action": "*"}]}}
- no_list_group_policy_names = {'PolicyNames': []}
- list_attached_policy_arn = {'AttachedPolicies': [{'PolicyArn': 'arn1', 'PolicyName': 'policyname1'},{'PolicyArn': 'arn2', 'PolicyName': 'policyname1'}]}
- get_policy = {'Policy': {'DefaultVersionId': 'v2'}}
- get_managed_policy_doc_allow = {"PolicyVersion": {"Document": {"Statement": [{"Effect": "Allow", "Action": "*"}]}}}
- get_managed_policy_doc_deny = {"PolicyVersion": {"Document": {"Statement": [{"Effect": "Deny", "Action": "*"}]}}}
-
- def test_non_compliant_inline(self):
- iam_client_mock.list_group_policies = MagicMock(return_value=self.list_group_policy_names)
- iam_client_mock.get_group_policy = MagicMock(return_value=self.get_group_policy_doc)
- lambdaEvent = build_lambda_configurationchange_event(invoking_event=self.invoking_event)
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'AIDAICVB3PKAQMPEGDW2C', annotation='An inline policy "policyname1" attached to the group "somegroupname" has full star allow permissions.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_non_compliant_managed(self):
- iam_client_mock.list_group_policies = MagicMock(return_value=self.no_list_group_policy_names)
- iam_client_mock.list_attached_group_policies = MagicMock(return_value=self.list_attached_policy_arn)
- iam_client_mock.get_policy = MagicMock(return_value=self.get_policy)
- iam_client_mock.get_policy_version = MagicMock(return_value=self.get_managed_policy_doc_allow)
- lambdaEvent = build_lambda_configurationchange_event(invoking_event=self.invoking_event)
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'AIDAICVB3PKAQMPEGDW2C', annotation='A managed policy with name "policyname1" attached to the group "somegroupname" has full star allow permissions.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_compliant_managed(self):
- iam_client_mock.list_group_policies = MagicMock(return_value=self.no_list_group_policy_names)
- iam_client_mock.list_attached_group_policies = MagicMock(return_value=self.list_attached_policy_arn)
- iam_client_mock.get_policy = MagicMock(return_value=self.get_policy)
- iam_client_mock.get_policy_version = MagicMock(return_value=self.get_managed_policy_doc_deny)
- lambdaEvent = build_lambda_configurationchange_event(invoking_event=self.invoking_event)
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'AIDAICVB3PKAQMPEGDW2C'))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(testClass, response, customerErrorCode=None, customerErrorMessage=None):
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- sts_client_mock.reset_mock(return_value=True)
- sts_client_mock.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_GROUP_NO_POLICY_FULL_STAR/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/IAM_GROUP_NO_POLICY_FULL_STAR/parameters.json
deleted file mode 100644
index dc0fe96..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_GROUP_NO_POLICY_FULL_STAR/parameters.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "IAM_GROUP_NO_POLICY_FULL_STAR",
- "SourceRuntime": "python3.6",
- "CodeKey": "IAM_GROUP_NO_POLICY_FULL_STAR.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::IAM::Group"
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_NO_USER/IAM_NO_USER.py b/vendor/github.com/awslabs/aws-config-rules/python/IAM_NO_USER/IAM_NO_USER.py
deleted file mode 100644
index a2e6b77..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_NO_USER/IAM_NO_USER.py
+++ /dev/null
@@ -1,423 +0,0 @@
-'''
-Rule Name:
- IAM_USER_NOT_ALLOWED_EXCEPT
-
-Description:
- To ensure there is no IAM users except if explicitly allowed.
-
-Rule Parameters:
- | ------------------- | --------- | ----------------------------------------------- |
- | Parameter Name | Type | Description |
- | ------------------- | --------- | ----------------------------------------------- |
- | WhitelistedUserList | Optional | List pf allowed IAM users, seperated by commas. |
- | ------------------- | --------- | ----------------------------------------------- |
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::::Account
-
-Feature:
- In order to: enforce our Security Policies
- As: a Security Officer
- I want: To ensure IAM users are not present unless from an allowed List.
-
-Scenarios:
-
- Scenario: 1
- Given: No IAM Users
- Then: Return Compliant
-
- Scenario: 2
- Given: WhitelistedUserList is not empty
- And: A user listed in WhitelistedUserList is not an alphanumerical string starting with "AIDA"
- Then: Return an error
-
- Scenario: 3
- Given: WhitelistedUserList is not empty
- And: each User ID is an alphanumerical string starting with "AIDA"
- And: All IAM Users UniqueID is listed in WhitelistedUserList
- Then: Return Compliant
-
- Scenario: 4
- Given: WhitelistedUserList is not empty
- And: WhitelistedUserList is configured correctly
- And: At least one IAM User UniqueID is not listed in WhitelistedUserList
- Then: Return Non-Compliant
-'''
-
-import json
-import datetime
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
-
- iam_client = get_client('iam', event)
- user_list = get_all_users(iam_client)
- for user in user_list:
- if user['UserId'] not in valid_rule_parameters:
- return build_evaluation(event['accountId'], 'NON_COMPLIANT', event, annotation='The user ({}) with id ({}) is not in the whitelist.'.format(user['UserName'], user['UserId']))
- return 'COMPLIANT'
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- valid_rule_parameters = []
- if "WhitelistUserList" in rule_parameters:
- whitelist = rule_parameters['WhitelistUserList'].split(',')
- for user in whitelist:
- user_id = user.strip()
- if not user_id.isupper():
- raise ValueError("UserId's cannot have lowercase letters: " + user_id)
- elif not user_id.isalnum():
- raise ValueError("UserId's must be alphanumeric: " + user_id)
- elif user_id[0:4] != 'AIDA':
- error_string = "UserId's must start with 'AIDA': "
- error_string += user_id
- error_string += ". Have you confused User ID's with User ARN's?"
- raise ValueError(error_string)
- elif user_id == 'AIDA':
- raise ValueError("UserId in whitelist is malformed: " + user_id)
- valid_rule_parameters.append(user_id)
-
- return valid_rule_parameters
-
-def get_all_users(client):
- list_to_return = []
- list = client.list_users()
- while True:
- for user in list['Users']:
- list_to_return.append(user)
- if 'Marker' in list:
- next_marker = list['Marker']
- list = client.list_users(Marker=next_marker)
- else:
- break
- return list_to_return
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Parameter value is invalid",
- internalErrorDetails="An ValueError was raised during the validation of the Parameter value",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluations, ResultToken=resultToken, TestMode=testMode)
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_NO_USER/IAM_NO_USER_test.py b/vendor/github.com/awslabs/aws-config-rules/python/IAM_NO_USER/IAM_NO_USER_test.py
deleted file mode 100644
index 0395a44..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_NO_USER/IAM_NO_USER_test.py
+++ /dev/null
@@ -1,311 +0,0 @@
-import sys
-import json
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-iam_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'sts':
- return sts_client_mock
- elif client_name == 'iam':
- return iam_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('IAM_NO_USER')
-
-#Scenario 2
-class TestInvalidParameters(unittest.TestCase):
- invalid_params_numeric_user_id = '{"WhitelistUserList":"12345"}'
- invalid_params_malformed_user_id = '{"WhitelistUserList":"ABCDEF12356"}'
- invalid_params_invalid_lowercase_user_id = '{"WhitelistUserList":"mbjfEF12356"}'
- invalid_params_short_user_id = '{"WhitelistUserList":"AIDA"}'
- invalid_params_disallowed_symbols_in_user_id = '{"WhitelistUserList":"AIDA*&903"}'
- invalid_params_space_in_user_id = '{"WhitelistUserList":"AIDA 2356ACBG"}'
- invalid_params_lowercase_user_id = '{"WhitelistUserList":"AIDA325ykvo"}'
- invalid_params_all_symbols_user_id = '{"WhitelistUserList":"(%$@!)"}'
- invalid_params_space_seperator = '{"WhitelistUserList":"AIDAJYPPIFB65RVYU7CCW AIDAJYPPIFB65RVYU7AAD"}'
- invalid_params_slash_separator = '{"WhitelistUserList":"AIDAJYPPIFB65RVYU7CCW/AIDAJYPPIFB65RVY9IP62"}'
- invalid_params_double_comma_separator = '{"WhitelistUserList":"AIDAJYPPIFB65RVYU7CCW,,AIDAJYPPILP90RVYU7WWC"}'
-
- def setUp(self):
- config_client_mock.reset_mock()
- iam_client_mock.reset_mock()
-
- def test_invalid_params_numeric_user_id(self):
- response = rule.lambda_handler(
- build_lambda_scheduled_event(self.invalid_params_numeric_user_id),
- {}
- )
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
- def test_invalid_params_malformed_user_id(self):
- response = rule.lambda_handler(
- build_lambda_scheduled_event(self.invalid_params_malformed_user_id),
- {}
- )
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
- def test_invalid_params_invalid_lowercase_user_id(self):
- response = rule.lambda_handler(
- build_lambda_scheduled_event(self.invalid_params_invalid_lowercase_user_id),
- {}
- )
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
- def test_invalid_params_short_user_id(self):
- response = rule.lambda_handler(
- build_lambda_scheduled_event(self.invalid_params_short_user_id),
- {}
- )
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
- def test_invalid_params_disallowed_symbols_in_user_id(self):
- response = rule.lambda_handler(
- build_lambda_scheduled_event(self.invalid_params_disallowed_symbols_in_user_id),
- {}
- )
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
- def test_invalid_params_space_in_user_id(self):
- response = rule.lambda_handler(
- build_lambda_scheduled_event(self.invalid_params_space_in_user_id),
- {}
- )
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
- def test_invalid_params_lowercase_user_id(self):
- response = rule.lambda_handler(
- build_lambda_scheduled_event(self.invalid_params_lowercase_user_id),
- {}
- )
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
- def test_invalid_params_all_symbols_user_id(self):
- response = rule.lambda_handler(
- build_lambda_scheduled_event(self.invalid_params_all_symbols_user_id),
- {}
- )
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
- def test_invalid_params_space_seperator(self):
- response = rule.lambda_handler(
- build_lambda_scheduled_event(self.invalid_params_space_seperator),
- {}
- )
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
- def test_invalid_params_slash_separator(self):
- response = rule.lambda_handler(
- build_lambda_scheduled_event(self.invalid_params_slash_separator),
- {}
- )
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
- def test_invalid_params_double_comma_separator(self):
- response = rule.lambda_handler(
- build_lambda_scheduled_event(self.invalid_params_double_comma_separator),
- {}
- )
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
-class TestScheduledExecution(unittest.TestCase):
-
- valid_params = '{"WhitelistUserList":"AIDAJYPPIFB65RVYU7CCW"}'
-
- list_users_no_results = {"Users": []}
- list_users_whitelisted_result_only = {"Users": [{"UserName": "some-user-name", "UserId":"AIDAJYPPIFB65RVYU7CCW"}]}
- list_users_mixed_whitelisted_result = {
- "Users": [
- {"UserName": "some-user-name-1", "UserId":"AIDAJYPPIFB65RVYU7CCW"},
- {"UserName": "some-user-name-2", "UserId":"AIDAJYPPIFB65RVYU7CCZ"}
- ]
- }
-
- def setUp(self):
- config_client_mock.reset_mock()
- iam_client_mock.reset_mock()
- rule.ASSUME_ROLE_MODE = False
-
- #Scenario 1
- def test_no_iam_users(self):
- iam_client_mock.list_users = MagicMock(return_value=self.list_users_no_results)
- response = rule.lambda_handler(build_lambda_scheduled_event(self.valid_params),{})
- resp_expected = []
- resp_expected.append(
- build_expected_response(
- 'COMPLIANT',
- '123456789012',
- 'AWS::::Account'
- )
- )
-
- assert_successful_evaluation(self, response, resp_expected)
-
- #Scenario 3
- def test_whitelisted_users_only(self):
- iam_client_mock.list_users = MagicMock(
- return_value=self.list_users_whitelisted_result_only
- )
- response = rule.lambda_handler(build_lambda_scheduled_event(self.valid_params),{})
- resp_expected = []
- resp_expected.append(
- build_expected_response(
- 'COMPLIANT',
- '123456789012',
- 'AWS::::Account'
- )
- )
-
- assert_successful_evaluation(self, response, resp_expected)
-
- #Scenario 4
- def test_mixed_whitelisted_users(self):
- iam_client_mock.list_users = MagicMock(
- return_value=self.list_users_mixed_whitelisted_result
- )
- response = rule.lambda_handler(build_lambda_scheduled_event(self.valid_params),{})
-
- resp_expected = []
- resp_expected.append(
- build_expected_response(
- 'NON_COMPLIANT',
- '123456789012',
- 'AWS::::Account',
- 'The user (some-user-name-2) with id (AIDAJYPPIFB65RVYU7CCZ) is not in the whitelist.'
- )
- )
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(testClass, response, customerErrorCode=None, customerErrorMessage=None):
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- sts_client_mock.reset_mock(return_value=True)
- sts_client_mock.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_NO_USER/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/IAM_NO_USER/parameters.json
deleted file mode 100644
index aa69c8e..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_NO_USER/parameters.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "IAM_NO_USER",
- "SourceRuntime": "python3.6",
- "CodeKey": "IAM_NO_USER.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{\"WhitelistedUserList\": \"\"}",
- "SourcePeriodic": "TwentyFour_Hours"
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_PASSWORD_POLICY/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/IAM_PASSWORD_POLICY/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_PASSWORD_POLICY/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_PASSWORD_POLICY/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/IAM_PASSWORD_POLICY/parameters.json
deleted file mode 100644
index 93a9932..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_PASSWORD_POLICY/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "IAM_PASSWORD_POLICY",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{\"RequireUppercaseCharacters\": \"true\", \"RequireLowercaseCharacters\": \"true\", \"RequireSymbols\": \"true\", \"RequireNumbers\": \"true\", \"MinimumPasswordLength\": \"14\", \"PasswordReusePrevention\": \"24\", \"MaxPasswordAge\": \"90\"}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "IAM_PASSWORD_POLICY"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_POLICY_REQUIRED/IAM_POLICY_REQUIRED.py b/vendor/github.com/awslabs/aws-config-rules/python/IAM_POLICY_REQUIRED/IAM_POLICY_REQUIRED.py
deleted file mode 100644
index 6ea5813..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_POLICY_REQUIRED/IAM_POLICY_REQUIRED.py
+++ /dev/null
@@ -1,521 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-"""
-#####################################
-## Gherkin ##
-#####################################
- Rule Name:
- IAM_POLICY_REQUIRED
- Description:
- To check IAM users and roles have a given policy attached directly or through a group.
- Trigger:
- Configuration Change on AWS::IAM::User/AWS::IAM::Role
- Reports on:
- AWS::IAM::User,AWS::IAM::Role
- Rule Parameters:
- | ---------------------- | --------- | -------------------------------------------------------- |
- | Parameter Name | Type | Description |
- | ---------------------- | --------- | -------------------------------------------------------- |
- | policyArns | Required | Comma separated list of policy ARNs which should be |
- | | | attached to users and roles. |
- | | | Example: "arn:aws:iam::012345678912:policy/MyPolicy" |
- | ---------------------- | --------- | -------------------------------------------------------- |
- | exceptionList | Optional | Represents the IAM users and roles which are exempted |
- | | | from the IAM Config rule. The valid entities in this list|
- | | | will be compliant by default. |
- | | | Example: users:[userName1,userName2],roles:[roleName] |
- | ---------------------- | --------- | -------------------------------------------------------- |
- Feature:
- As: a Security Officer
- I want: To ensure that IAM roles and users have mandatory policies attached
- In order to: enforce mandatory permissions for IAM entities
- Scenarios:
- Scenario: 1
- Given: No IAM Users or Roles exist
- When: Evaluation occurs
- Then: Return "NOT_APPLICABLE"
- Scenario: 2
- Given: exceptionList is configured
- And: An listed in the exceptionList is not an alphanumerical string
- When: Evaluation occurs
- Then: Return an error
- Examples:
- | entity |
- | IAM User |
- | IAM Role |
- Scenario: 3
- Given: policyArns is configured
- And: policyArns does not contain valid ARNs
- Then: Return an error
- Scenario: 4
- Given: An exists
- And: exceptionList is configured and valid
- And: The is listed in the exceptionList
- When: Evaluation occurs
- Then: Return COMPLIANT
- Examples:
- | entity |
- | IAM User |
- | IAM Role |
- Scenario: 5
- Given: An exists
- And: The has all the policies listed in policyArns attached
- When: Evaluation occurs
- Then: Return COMPLIANT
- Examples:
- | entity |
- | IAM User |
- | IAM Role |
- Scenario: 6
- Given: An IAM user exists
- And: The IAM user groups combined have the policies listed in policyArns attached
- When: Evaluation occurs
- Then: Return COMPLIANT
- Scenario: 7
- Given: An IAM user exists
- And: The IAM user does not have all the policies listed in policyArns attached
- And: The IAM user's groups combined do not have all the policy listed in policyArns attached
- When: Evaluation occurs
- Then: return NON_COMPLIANT
- Scenario: 8
- Given: An IAM role exists
- And: The IAM role does not have all the policies listed in policyArns attached
- When: Evaluation occurs
- Then: return NON_COMPLIANT
-"""
-
-import json
-import re
-import datetime
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::IAM::Role'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-
-def should_ignore_config_item(config_item, ignored_roles, ignored_users):
- return (config_item['resourceType'] == 'AWS::IAM::Role' and config_item['resourceName'] in ignored_roles) \
- or (config_item['resourceType'] == 'AWS::IAM::User' and config_item['resourceName'] in ignored_users) \
- or (config_item['ARN'].rsplit("/")[1] == 'aws-service-role')
-
-
-def get_attached_policies(configuration_item):
- # Get the users managed policies
- attach_policies = [
- policy["policyArn"] for policy in configuration_item["configuration"].get("attachedManagedPolicies")
- ]
-
- return attach_policies
-
-
-def list_contains_all(source_list, items):
- return all(policy in source_list for policy in items)
-
-
-def paginate(client, method, **kwargs):
- paginator = client.get_paginator(method.__name__)
- for page in paginator.paginate(**kwargs).result_key_iters():
- for result in page:
- yield result
-
-
-def has_policy_attached(event, configuration_item, policy_arns):
- resource_type = configuration_item['resourceType']
- if resource_type == 'AWS::IAM::User':
- managed_policies = get_attached_policies(configuration_item)
- if list_contains_all(managed_policies, policy_arns):
- return True
- # Additively check the users groups to see if they have the required policies
- client = get_client('iam', event)
- groups = configuration_item["configuration"].get("groupList", [])
- for group in groups:
- attached_policies = paginate(client, client.list_attached_group_policies, **{'GroupName': group})
- for policy in attached_policies:
- managed_policies.append(policy['PolicyArn'])
- if list_contains_all(managed_policies, policy_arns):
- return True
-
- return False
- elif resource_type == 'AWS::IAM::Role':
- return list_contains_all(get_attached_policies(configuration_item), policy_arns)
-
- raise ValueError('Unable to handle resource type {}'.format(resource_type))
-
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
- policy_arns = valid_rule_parameters['policyArns']
- exception_list = valid_rule_parameters["exceptionList"]
- ignored_roles = exception_list["roles"]
- ignored_users = exception_list["users"]
-
- if should_ignore_config_item(configuration_item, ignored_roles, ignored_users):
- return build_evaluation_from_config_item(configuration_item, 'COMPLIANT', 'Ignored IAM entity')
- elif has_policy_attached(event, configuration_item, policy_arns):
- return build_evaluation_from_config_item(configuration_item, 'COMPLIANT', 'All expected policies attached')
-
- return build_evaluation_from_config_item(configuration_item, 'NON_COMPLIANT', 'IAM entity missing policies')
-
-
-def is_valid_arn(arn):
- pattern = re.compile("arn:(aws[a-zA-Z-]*)?:iam::(aws|\d{12}):policy\/[a-zA-Z0-9-_\/]+")
- return pattern.match(arn)
-
-
-def extract_entities_from_exception_list(entity_type, exception_list):
- pattern = re.compile("{Type}:\s?\[([a-zA-Z0-9-_,]+)\]".format(Type=entity_type))
- matches = pattern.search(exception_list)
- if matches:
- return matches.group(1).replace(' ', '').split(",")
- return []
-
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- policy_arns = rule_parameters.get("policyArns", "").split(",")
- if not all(is_valid_arn(arn) for arn in policy_arns):
- raise ValueError('Invalid policy ARNs specified in policyArns')
-
- exception_list = rule_parameters.get("exceptionList", "")
-
- return {
- 'policyArns': policy_arns,
- 'exceptionList': {
- 'users': extract_entities_from_exception_list('users', exception_list),
- 'roles': extract_entities_from_exception_list('roles', exception_list),
- }
- }
-
-####################
-# Helper Functions #
-####################
-
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Parameter value is invalid",
- internalErrorDetails="An ValueError was raised during the validation of the Parameter value",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluations, ResultToken=resultToken, TestMode=testMode)
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_POLICY_REQUIRED/IAM_POLICY_REQUIRED_test.py b/vendor/github.com/awslabs/aws-config-rules/python/IAM_POLICY_REQUIRED/IAM_POLICY_REQUIRED_test.py
deleted file mode 100644
index 4400871..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_POLICY_REQUIRED/IAM_POLICY_REQUIRED_test.py
+++ /dev/null
@@ -1,369 +0,0 @@
-import json
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::IAM::Role'
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-iam_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'sts':
- return sts_client_mock
- elif client_name == 'iam':
- return iam_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-
-def build_user_configuration_item(attached_policies_arns, user_groups, user_name="test-user"):
- return {
- "configurationItemDiff": None,
- "configurationItem": {
- "relatedEvents": [],
- "relationships": [
- {
- "resourceId": "ABCDEFGHI12JKL4MNO5PQ",
- "resourceName": arn.rsplit("/")[-1],
- "resourceType": "AWS::IAM::Policy",
- "name": "Is attached to CustomerManagedPolicy"
- } for arn in attached_policies_arns
- ],
- "configuration": {
- "groupList": list(user_groups.keys()),
- "userName": user_name,
- "userId": "ABCDEFGHI12JKL4MNO5PQ",
- "arn": "arn:aws:iam::0123456789012:user{Name}".format(Name=user_name),
- "createDate": "2018-08-30T13:25:10.000Z",
- "assumeRolePolicyDocument": "{}",
- "instanceProfileList": [],
- "rolePolicyList": [],
- "attachedManagedPolicies": [
- {
- "policyName": arn.rsplit("/")[-1],
- "policyArn": arn
- } for arn in attached_policies_arns
- ],
- "permissionsBoundary": None
- },
- "supplementaryConfiguration": {},
- "tags": {},
- "configurationItemVersion": "1.3",
- "configurationItemCaptureTime": "2018-09-07T11:24:32.476Z",
- "configurationStateId": 12345678901,
- "awsAccountId": "0123456789012",
- "configurationItemStatus": "OK",
- "resourceType": "AWS::IAM::User",
- "resourceId": "ABCDEFGHI12JKL4MNO5PQ",
- "resourceName": user_name,
- "ARN": "arn:aws:iam::0123456789012:user/{}".format(user_name),
- "awsRegion": "global",
- "availabilityZone": "Not Applicable",
- "configurationStateMd5Hash": "",
- "resourceCreationTime": "2018-08-30T13:25:10.000Z"
- },
- "notificationCreationTime": "2018-09-21T09:24:28.962Z",
- "messageType": "ConfigurationItemChangeNotification",
- "recordVersion": "1.3"
- }
-
-
-def build_role_configuration_item(attached_policies_arns, role_name="test-role", path="/"):
- return {
- "configurationItemDiff": None,
- "configurationItem": {
- "relatedEvents": [],
- "relationships": [
- {
- "resourceId": "ABCDEFGHI12JKL4MNO5PQ",
- "resourceName": arn.rsplit("/")[-1],
- "resourceType": "AWS::IAM::Policy",
- "name": "Is attached to CustomerManagedPolicy"
- } for arn in attached_policies_arns
- ],
- "configuration": {
- "path": path,
- "roleName": role_name,
- "roleId": "ABCDEFGHI12JKL4MNO5PQ",
- "arn": "arn:aws:iam::0123456789012:role{Path}{Name}".format(Path=path, Name=role_name),
- "createDate": "2018-08-30T13:25:10.000Z",
- "assumeRolePolicyDocument": "{}",
- "instanceProfileList": [],
- "rolePolicyList": [],
- "attachedManagedPolicies": [
- {
- "policyName": arn.rsplit("/")[-1],
- "policyArn": arn
- } for arn in attached_policies_arns
- ],
- "permissionsBoundary": None
- },
- "supplementaryConfiguration": {},
- "tags": {},
- "configurationItemVersion": "1.3",
- "configurationItemCaptureTime": "2018-09-07T11:24:32.476Z",
- "configurationStateId": 12345678901,
- "awsAccountId": "0123456789012",
- "configurationItemStatus": "OK",
- "resourceType": "AWS::IAM::Role",
- "resourceId": "ABCDEFGHI12JKL4MNO5PQ",
- "resourceName": role_name,
- "ARN": "arn:aws:iam::0123456789012:role/{}".format(role_name),
- "awsRegion": "global",
- "availabilityZone": "Not Applicable",
- "configurationStateMd5Hash": "",
- "resourceCreationTime": "2018-08-30T13:25:10.000Z"
- },
- "notificationCreationTime": "2018-09-21T09:24:28.962Z",
- "messageType": "ConfigurationItemChangeNotification",
- "recordVersion": "1.3"
- }
-
-
-rule = __import__('IAM_POLICY_REQUIRED')
-
-class TestPolicyRequired(unittest.TestCase):
-
- rule_parameters = '{"policyArns":"arn:aws:iam::aws:policy/AdministratorAccess", "exceptionList": ""}'
- invoking_event_iam_role_sample = json.dumps(build_role_configuration_item(['arn:aws:iam::aws:policy/AdministratorAccess']))
- invoking_event_iam_user_sample = json.dumps(build_user_configuration_item([
- 'arn:aws:iam::aws:policy/AdministratorAccess'], {'group1': [{'PolicyArn':'arn:aws:iam::aws:policy/AdministratorAccess'}]}))
-
- def test_it_marks_service_roles_as_compliant(self):
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_configurationchange_event(
- self.invoking_event_iam_role_sample.replace("role/", "role/aws-service-role/"), self.rule_parameters), {})
- resp_expected = [build_expected_response('COMPLIANT', ANY, 'AWS::IAM::Role', ANY)]
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_it_marks_ignored_roles_as_compliant(self):
- rule_parameters = '{"policyArns":"arn:aws:iam::aws:policy/AdministratorAccess", "exceptionList": "users:[test-user],roles:[test-role]"}'
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_configurationchange_event(
- self.invoking_event_iam_role_sample, rule_parameters), {})
- resp_expected = [build_expected_response('COMPLIANT', ANY, 'AWS::IAM::Role', ANY)]
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_it_marks_ignored_users_as_compliant(self):
- rule_parameters = '{"policyArns":"arn:aws:iam::aws:policy/AdministratorAccess", "exceptionList": "users:[test-user]"}'
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(build_lambda_configurationchange_event(
- self.invoking_event_iam_user_sample, rule_parameters), {})
- resp_expected = [build_expected_response('COMPLIANT', ANY, 'AWS::IAM::User', ANY)]
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_it_marks_roles_with_policy_as_compliant(self):
- invoking_event = json.dumps(build_role_configuration_item(['arn:aws:iam::aws:policy/AdministratorAccess']))
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(
- build_lambda_configurationchange_event(invoking_event, self.rule_parameters), {})
- resp_expected = [build_expected_response('COMPLIANT', 'ABCDEFGHI12JKL4MNO5PQ', 'AWS::IAM::Role', ANY)]
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_it_marks_users_with_policy_as_compliant(self):
- invoking_event = json.dumps(build_user_configuration_item([
- 'arn:aws:iam::aws:policy/AdministratorAccess'], {}))
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(
- build_lambda_configurationchange_event(invoking_event, self.rule_parameters), {})
-
- resp_expected = [build_expected_response('COMPLIANT', 'ABCDEFGHI12JKL4MNO5PQ', 'AWS::IAM::User', ANY)]
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_it_marks_users_in_group_with_policy_as_compliant(self):
- invoking_event = json.dumps(build_user_configuration_item(
- [], {'group1': ['arn:aws:iam::aws:policy/AdministratorAccess']}))
- rule.ASSUME_ROLE_MODE = False
- iam_client_mock.configure_mock(**{
- "get_paginator.return_value": iam_client_mock,
- "paginate.return_value": iam_client_mock,
- "result_key_iters.return_value": [
- [{
- 'PolicyName': 'AdministratorAccess',
- 'PolicyArn': 'arn:aws:iam::aws:policy/AdministratorAccess',
- }]
- ]
- })
- iam_client_mock.list_attached_group_policies.__name__ = 'list_attached_group_policies'
- response = rule.lambda_handler(
- build_lambda_configurationchange_event(invoking_event, self.rule_parameters), {})
-
- resp_expected = [build_expected_response('COMPLIANT', 'ABCDEFGHI12JKL4MNO5PQ', 'AWS::IAM::User', ANY)]
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_it_marks_users_without_policy_as_non_compliant(self):
- invoking_event = json.dumps(build_user_configuration_item([], {}))
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(
- build_lambda_configurationchange_event(invoking_event, self.rule_parameters), {})
-
- resp_expected = [build_expected_response('NON_COMPLIANT', 'ABCDEFGHI12JKL4MNO5PQ', 'AWS::IAM::User', ANY)]
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_it_marks_roles_without_policy_as_non_compliant(self):
- invoking_event = json.dumps(build_role_configuration_item([]))
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(
- build_lambda_configurationchange_event(invoking_event, self.rule_parameters), {})
-
- resp_expected = [build_expected_response('NON_COMPLIANT', 'ABCDEFGHI12JKL4MNO5PQ', 'AWS::IAM::Role', ANY)]
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_it_handles_customer_managed_policy_arns(self):
- invoking_event = json.dumps(build_role_configuration_item(
- ['arn:aws:iam::012345678912:policy/my-policy']))
- rule_parameters = '{"policyArns":"arn:aws:iam::012345678912:policy/my-policy", "exceptionList": ""}'
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(
- build_lambda_configurationchange_event(invoking_event, rule_parameters), {})
-
- resp_expected = [build_expected_response('COMPLIANT', 'ABCDEFGHI12JKL4MNO5PQ', 'AWS::IAM::Role', ANY)]
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_it_handles_customer_managed_policy_arns_with_paths(self):
- invoking_event = json.dumps(build_role_configuration_item(
- ['arn:aws:iam::012345678912:policy/my-path/my-policy']))
- rule_parameters = '{"policyArns":"arn:aws:iam::012345678912:policy/my-path/my-policy", "exceptionList": ""}'
- rule.ASSUME_ROLE_MODE = False
- response = rule.lambda_handler(
- build_lambda_configurationchange_event(invoking_event, rule_parameters), {})
-
- resp_expected = [build_expected_response('COMPLIANT', 'ABCDEFGHI12JKL4MNO5PQ', 'AWS::IAM::Role', ANY)]
- assert_successful_evaluation(self, response, resp_expected)
-
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(testClass, response, customerErrorCode=None, customerErrorMessage=None):
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- sts_client_mock.reset_mock(return_value=True)
- sts_client_mock.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- invoking_event = json.dumps(build_role_configuration_item(
- ['arn:aws:iam::012345678912:policy/my-policy']))
- rule_parameters = '{"policyArns":"arn:aws:iam::012345678912:policy/my-policy", "exceptionList": ""}'
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event(invoking_event, rule_parameters), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- invoking_event = json.dumps(build_role_configuration_item(
- ['arn:aws:iam::012345678912:policy/my-policy']))
- rule_parameters = '{"policyArns":"arn:aws:iam::012345678912:policy/my-policy", "exceptionList": ""}'
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event(invoking_event, rule_parameters), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_POLICY_REQUIRED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/IAM_POLICY_REQUIRED/parameters.json
deleted file mode 100644
index 85aa640..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_POLICY_REQUIRED/parameters.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "CodeKey": "IAM_POLICY_REQUIRED.zip",
- "SourceRuntime": "python3.6",
- "RuleName": "IAM_POLICY_REQUIRED",
- "SourceEvents": "AWS::IAM::Role,AWS::IAM::User",
- "OptionalParameters": "{\"exceptionList\": \"\"}",
- "InputParameters": "{\"policyArns\": \"\"}"
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_ROLE_NO_POLICY_FULL_STAR/IAM_ROLE_NO_POLICY_FULL_STAR.py b/vendor/github.com/awslabs/aws-config-rules/python/IAM_ROLE_NO_POLICY_FULL_STAR/IAM_ROLE_NO_POLICY_FULL_STAR.py
deleted file mode 100644
index 0af26e1..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_ROLE_NO_POLICY_FULL_STAR/IAM_ROLE_NO_POLICY_FULL_STAR.py
+++ /dev/null
@@ -1,460 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-'''
-#####################################
-## Gherkin ##
-#####################################
-
- Rule Name:
- iam-role-no-policy-full-star
-
- Description:
- Check whether IAM Roles have an IAM policy with all permission enabled (or "Action:*"). IAM Policy includes the AWS-managed policy named AdministratorAccess, Customer-managed policies and inline policies.
-
- Trigger:
- Configuration Change on AWS::IAM::Role
-
- Reports on:
- AWS::IAM::Role
-
- Rule Parameters:
- None
-
- Feature:
- In order to: ensure that policies attached to any role does not have full administrative privileges
- As: a Security Officer
- I want: to ensure that no policy have "*" in their action.
-
- Scenarios:
- Scenario 1:
- Given: Any allow statements of role has Action as "*"
- Then: Return NON_COMPLIANT
-
- Scenario 2:
- Given: All allow statments of role do not have Action as "*"
- Then: Return COMPLIANT
-
- Scenario 3:
- Given: No policy is applying to the role
- Then: Return COMPLIANT
-
- Examples:
- | Policy |
- | inline policy |
- | aws-managed predefined policy |
- | customer-managed defined policy |
-'''
-import json
-import datetime
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::IAM::Role'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
-
- role_name = configuration_item['configuration']['roleName']
- iam_client = get_client('iam', event)
-
- # Inline policies
- inline_policy_names = get_all_role_inline_policy_names(iam_client, role_name)
- for policy_name in inline_policy_names:
- policy_document = iam_client.get_role_policy(RoleName=role_name, PolicyName=policy_name)['PolicyDocument']
- if is_statements_include_full_star_allow(policy_document['Statement']):
- return build_evaluation_from_config_item(configuration_item, "NON_COMPLIANT", annotation='An inline policy "' + policy_name + '" attached to the role "' + role_name + '" has full star allow permissions.')
-
- # Managed policies
- managed_policy_arn_and_name = get_all_role_managed_policy_arn_and_name(iam_client, role_name)
- for policy_arn, policy_name in managed_policy_arn_and_name.items():
- get_policy = iam_client.get_policy(PolicyArn=policy_arn)
- version = get_policy['Policy']['DefaultVersionId']
- get_policy_version = iam_client.get_policy_version(PolicyArn=policy_arn, VersionId=version)
- if is_statements_include_full_star_allow(get_policy_version['PolicyVersion']['Document']['Statement']):
- return build_evaluation_from_config_item(configuration_item, "NON_COMPLIANT", annotation='A managed policy with name "' + policy_name + '" attached to the role "' + role_name + '" has full star allow permissions.')
-
- return "COMPLIANT"
-
-def get_all_role_inline_policy_names(iam_client, role_name):
- all_role_inline_policies = []
- list_policy_names = iam_client.list_role_policies(RoleName=role_name, MaxItems=1000)
- while True:
- all_role_inline_policies += list_policy_names['PolicyNames']
- if 'Marker' in list_policy_names:
- list_policy_names = iam_client.list_role_policies(RoleName=role_name, MaxItems=1000, Marker=list_policy_names['Marker'])
- else:
- break
- return all_role_inline_policies
-
-def get_all_role_managed_policy_arn_and_name(iam_client, role_name):
- all_role_managed_policies_arn_and_name = {}
- list_policy_arn = iam_client.list_attached_role_policies(RoleName=role_name, MaxItems=1000)
- while True:
- for policy_dict in list_policy_arn['AttachedPolicies']:
- all_role_managed_policies_arn_and_name[policy_dict['PolicyArn']] = policy_dict['PolicyName']
- if 'Marker' in list_policy_arn:
- list_policy_arn = iam_client.list_attached_role_policies(RoleName=role_name, MaxItems=1000, Marker=list_policy_arn['Marker'])
- else:
- break
- return all_role_managed_policies_arn_and_name
-
-def is_statements_include_full_star_allow(statements):
- statement_list = []
- if isinstance(statements, dict):
- statement_list = [statements]
- elif isinstance(statements, list):
- statement_list = statements
- else:
- print("Not recognized statement type:")
- print(statements)
- return False
-
- for statement in statement_list:
- if statement['Effect'] == 'Deny':
- continue
-
- if 'Action' not in statement:
- print("No 'Action' in statement")
- print(statement)
- continue
-
- if isinstance(statement['Action'], list):
- for action in statement['Action']:
- if action == "*":
- return True
- else:
- if statement['Action'] == "*":
- return True
- return False
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Parameter value is invalid",
- internalErrorDetails="An ValueError was raised during the validation of the Parameter value",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluations, ResultToken=resultToken, TestMode=testMode)
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_ROLE_NO_POLICY_FULL_STAR/IAM_ROLE_NO_POLICY_FULL_STAR_test.py b/vendor/github.com/awslabs/aws-config-rules/python/IAM_ROLE_NO_POLICY_FULL_STAR/IAM_ROLE_NO_POLICY_FULL_STAR_test.py
deleted file mode 100644
index 6bbb209..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_ROLE_NO_POLICY_FULL_STAR/IAM_ROLE_NO_POLICY_FULL_STAR_test.py
+++ /dev/null
@@ -1,196 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::IAM::Role'
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-iam_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'sts':
- return sts_client_mock
- elif client_name == 'iam':
- return iam_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('IAM_ROLE_NO_POLICY_FULL_STAR')
-
-class ComplianceTest(unittest.TestCase):
-
- invoking_event = '{"configurationItemDiff":"SomeDifference", "notificationCreationTime":"SomeTime", "messageType":"ConfigurationItemChangeNotification", "recordVersion":"SomeVersion", "configurationItem":{ "resourceType":"AWS::IAM::Role","configurationItemStatus":"ResourceDiscovered", "resourceId":"AIDAICVB3PKAQMPEGDW2C", "configurationItemCaptureTime":"2018-02-20T06:56:55.533Z", "configuration":{"roleName": "somerolename"}}}'
-
- list_role_policy_names = {'PolicyNames': ['policyname1', 'policyname2']}
- get_role_policy_doc = {'PolicyDocument': {"Statement": [{"Effect": "Allow", "Action": "*"}]}}
- no_list_role_policy_names = {'PolicyNames': []}
- list_attached_policy_arn = {'AttachedPolicies': [{'PolicyArn': 'arn1', 'PolicyName': 'policyname1'},{'PolicyArn': 'arn2', 'PolicyName': 'policyname1'}]}
- get_policy = {'Policy': {'DefaultVersionId': 'v2'}}
- get_managed_policy_doc_allow = {"PolicyVersion": {"Document": {"Statement": [{"Effect": "Allow", "Action": "*"}]}}}
- get_managed_policy_doc_deny = {"PolicyVersion": {"Document": {"Statement": [{"Effect": "Deny", "Action": "*"}]}}}
-
- def test_non_compliant_inline(self):
- iam_client_mock.list_role_policies = MagicMock(return_value=self.list_role_policy_names)
- iam_client_mock.get_role_policy = MagicMock(return_value=self.get_role_policy_doc)
- lambdaEvent = build_lambda_configurationchange_event(invoking_event=self.invoking_event)
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'AIDAICVB3PKAQMPEGDW2C', annotation='An inline policy "policyname1" attached to the role "somerolename" has full star allow permissions.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_non_compliant_managed(self):
- iam_client_mock.list_role_policies = MagicMock(return_value=self.no_list_role_policy_names)
- iam_client_mock.list_attached_role_policies = MagicMock(return_value=self.list_attached_policy_arn)
- iam_client_mock.get_policy = MagicMock(return_value=self.get_policy)
- iam_client_mock.get_policy_version = MagicMock(return_value=self.get_managed_policy_doc_allow)
- lambdaEvent = build_lambda_configurationchange_event(invoking_event=self.invoking_event)
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'AIDAICVB3PKAQMPEGDW2C', annotation='A managed policy with name "policyname1" attached to the role "somerolename" has full star allow permissions.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_compliant_managed(self):
- iam_client_mock.list_role_policies = MagicMock(return_value=self.no_list_role_policy_names)
- iam_client_mock.list_attached_role_policies = MagicMock(return_value=self.list_attached_policy_arn)
- iam_client_mock.get_policy = MagicMock(return_value=self.get_policy)
- iam_client_mock.get_policy_version = MagicMock(return_value=self.get_managed_policy_doc_deny)
- lambdaEvent = build_lambda_configurationchange_event(invoking_event=self.invoking_event)
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'AIDAICVB3PKAQMPEGDW2C'))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(testClass, response, customerErrorCode=None, customerErrorMessage=None):
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- sts_client_mock.reset_mock(return_value=True)
- sts_client_mock.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_ROLE_NO_POLICY_FULL_STAR/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/IAM_ROLE_NO_POLICY_FULL_STAR/parameters.json
deleted file mode 100644
index 8325306..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_ROLE_NO_POLICY_FULL_STAR/parameters.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "IAM_ROLE_NO_POLICY_FULL_STAR",
- "SourceRuntime": "python3.6",
- "CodeKey": "IAM_ROLE_NO_POLICY_FULL_STAR.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::IAM::Role",
- "RuleSets": [
- "baseline",
- "rulecriticity:high",
- "pci",
- "pci:7.1",
- "iam"
- ]
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_MATCHES_REGEX_PATTERN/IAM_USER_MATCHES_REGEX_PATTERN.py b/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_MATCHES_REGEX_PATTERN/IAM_USER_MATCHES_REGEX_PATTERN.py
deleted file mode 100644
index 420c24f..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_MATCHES_REGEX_PATTERN/IAM_USER_MATCHES_REGEX_PATTERN.py
+++ /dev/null
@@ -1,410 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-'''
-#####################################
-## Gherkin ##
-#####################################
-Rule Name:
- IAM_USER_MATCHES_REGEX_PATTERN
-Description:
- Verify that IAM user name conforms to a particular regex pattern.
-Trigger:
- Configuration change on AWS::IAM::User
-Resource Type to report on:
- AWS::IAM::User
-Rule Parameters:
- regexPattern (Required)
- Find the specified regex pattern in the IAM user name.
-Scenarios:
- Scenario 1:
- Given: Regex Pattern is not specified
- Then: Return Error
- Scenario 2:
- Given: Regex Pattern an invaild regex pattern
- Then: Return Error
- Scenario 3:
- Given: The parameter regexPattern is configured and valid.
- And: A user name contains the regex pattern
- Then: COMPLIANT
- Scenario 4:
- Given: Regex Pattern
- And: A user name does not contain the regex pattern
- Then: Return NON_COMPLIANT
-'''
-
-import json
-import re
-import sys
-import datetime
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::IAM::User'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
- """
- iam_user_name = configuration_item["resourceName"]
- pattern = valid_rule_parameters["regexPattern"]
- is_compliant = evaluate_username(iam_user_name, pattern)
-
- if is_compliant:
- return build_evaluation(iam_user_name, 'COMPLIANT', event)
-
- return build_evaluation(iam_user_name, 'NON_COMPLIANT', event, annotation='The regex ({}) does not match ({}).'.format(pattern, iam_user_name))
-
-def evaluate_username(user_name, pattern_str):
- """Evaluate the regex pattern for match in the user name
-
- Return:
- True when regex pattern does match
-
- Keyword arguments:
- user_name -- the string in the iam user name
- pattern_str -- a string that gets compiled into a regular expression object
- """
- pattern = re.compile(f"{pattern_str}")
- return pattern.match(user_name)
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- try:
- re.compile(rule_parameters['regexPattern'])
- except:
- raise ValueError('The Config Rule must have a validate regex value specified for the parameter "regexPattern"')
-
- return rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status in ('OK', 'ResourceDiscovered')) and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::IAM::User'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_MATCHES_REGEX_PATTERN/IAM_USER_MATCHES_REGEX_PATTERN_test.py b/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_MATCHES_REGEX_PATTERN/IAM_USER_MATCHES_REGEX_PATTERN_test.py
deleted file mode 100644
index 7fe127d..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_MATCHES_REGEX_PATTERN/IAM_USER_MATCHES_REGEX_PATTERN_test.py
+++ /dev/null
@@ -1,225 +0,0 @@
-'''
- This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
- Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
- Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-'''
-import sys
-import json
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::IAM::User'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-IAM_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
-
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('IAM_USER_MATCHES_REGEX_PATTERN')
-
-class ComplianceTest(unittest.TestCase):
-
- user_list = {'Users': [
- {'UserId': 'AIDAIDFOUX2OSRO6DO7XA',
- 'UserName': 'user-name-0'},
- {'UserId': 'AIDAIDFOUX2OSRO6DO7XB',
- 'UserName': 'user-name-admin-1'},
- {'UserId': 'AIDAIDFOUX2OSRO6DO7XF',
- 'UserName': 'Admin-user-badexpress-2'},
- {'UserId': 'AIDAIDFOUX2OSRO6DO7XG',
- 'UserName': 'Admin-user-no-pattern-3'}]}
-
- def test_scenario_1_no_pattern(self):
- """Test scenario to test when pattern is not specified
- Keyword arguments:
- self -- class ComplianceTest
- """
- IAM_CLIENT_MOCK.list_users = MagicMock(return_value=self.user_list)
- rule_param = {}
- invoking_event = construct_invoking_event(construct_config_item(self.user_list['Users'][3]['UserName']))
- lambda_event = build_lambda_configurationchange_event(invoking_event, rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- print(response)
- assert_customer_error_response(self, response, "InvalidParameterValueException")
-
- def test_scenario_2_invalid_regex(self):
- """Test scenario to test an invaild regex pattern
- Keyword arguments:
- self -- class ComplianceTest
- """
- IAM_CLIENT_MOCK.list_users = MagicMock(return_value=self.user_list)
- rule_param = {"regexPattern":"[bad"}
- invoking_event = construct_invoking_event(construct_config_item(self.user_list['Users'][2]['UserName']))
- lambda_event = build_lambda_configurationchange_event(invoking_event, rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- print(response)
- assert_customer_error_response(self, response, "InvalidParameterValueException")
-
- def test_scenario_3_non_compliant(self):
- """Test scenario to test non-compliant user name
- Keyword arguments:
- self -- class ComplianceTest
- """
- IAM_CLIENT_MOCK.list_users = MagicMock(return_value=self.user_list)
- rule_param = {"regexPattern":"admin"}
- invoking_event = construct_invoking_event(construct_config_item(self.user_list['Users'][0]['UserName']))
- lambda_event = build_lambda_configurationchange_event(invoking_event, rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- print(response)
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', self.user_list['Users'][0]['UserName'], annotation='The regex (admin) does not match (user-name-0).'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario_4_compliant(self):
- """Test scenario to test compliant user name
- Keyword arguments:
- self -- class ComplianceTest
- """
- IAM_CLIENT_MOCK.list_users = MagicMock(return_value=self.user_list)
- rule_param = {"regexPattern":".*admin.*"}
- invoking_event = construct_invoking_event(construct_config_item(self.user_list['Users'][1]['UserName']))
- lambda_event = build_lambda_configurationchange_event(invoking_event, rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- print(response)
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', self.user_list['Users'][1]['UserName']))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def construct_config_item(resource_name):
- config_item = {
- 'relatedEvents': [],
- 'relationships': [],
- 'configuration': None,
- 'configurationItemVersion': None,
- 'configurationItemCaptureTime': "2019-03-17T03:37:52.418Z",
- 'supplementaryConfiguration': {},
- 'configurationStateId': 1532049940079,
- 'awsAccountId': "SAMPLE",
- 'configurationItemStatus': "ResourceDiscovered",
- 'resourceType': "AWS::IAM::User",
- 'resourceId': "AIDAILEDWOGIPJFAKOJKW",
- 'resourceName': resource_name,
- 'ARN': "arn:aws:iam::264683526309:user/{}".format(resource_name),
- 'awsRegion': "ap-south-1",
- 'configurationStateMd5Hash': "",
- 'resourceCreationTime': "2019-03-17T06:27:28.289Z",
- 'tags': {}
- }
- return config_item
-
-def construct_invoking_event(config_item):
- invoking_event = {
- "configurationItemDiff": None,
- "configurationItem": config_item,
- "notificationCreationTime": "SAMPLE",
- "messageType": "ConfigurationItemChangeNotification",
- "recordVersion": "SAMPLE"
- }
- return invoking_event
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': json.dumps(invoking_event),
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = json.dumps(rule_parameters)
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_MATCHES_REGEX_PATTERN/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_MATCHES_REGEX_PATTERN/parameters.json
deleted file mode 100644
index eaaa113..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_MATCHES_REGEX_PATTERN/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "IAM_USER_MATCHES_REGEX_PATTERN",
- "SourceRuntime": "python3.6",
- "CodeKey": "IAM_USER_MATCHES_REGEX_PATTERN.zip",
- "InputParameters": "{\"regexPattern\":\".*admin.*\"}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::IAM::User"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_MFA_ENABLED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_MFA_ENABLED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_MFA_ENABLED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_MFA_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_MFA_ENABLED/parameters.json
deleted file mode 100644
index 1fabdc7..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_MFA_ENABLED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "IAM_USER_MFA_ENABLED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "IAM_USER_MFA_ENABLED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_NO_POLICY_FULL_STAR/IAM_USER_NO_POLICY_FULL_STAR.py b/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_NO_POLICY_FULL_STAR/IAM_USER_NO_POLICY_FULL_STAR.py
deleted file mode 100644
index cec1aab..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_NO_POLICY_FULL_STAR/IAM_USER_NO_POLICY_FULL_STAR.py
+++ /dev/null
@@ -1,460 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-'''
-#####################################
-## Gherkin ##
-#####################################
-
- Rule Name:
- iam-user-no-policy-full-star
-
- Description:
- Check whether IAM Users have an IAM policy with all permission enabled (or "Action:*"). IAM Policy includes the AWS-managed policy named AdministratorAccess, Customer-managed policies and inline policies.
-
- Trigger:
- Configuration Change on AWS::IAM::User
-
- Reports on:
- AWS::IAM::User
-
- Rule Parameters:
- None
-
- Feature:
- In order to: ensure that policies attached to any user does not have full administrative privileges
- As: a Security Officer
- I want: to ensure that no policy have "*" in their action.
-
- Scenarios:
- Scenario 1:
- Given: Any allow statements of user has Action as "*"
- Then: Return NON_COMPLIANT
-
- Scenario 2:
- Given: All allow statments of user do not have Action as "*"
- Then: Return COMPLIANT
-
- Scenario 3:
- Given: No policy is applying to the user
- Then: Return COMPLIANT
-
- Examples:
- | Policy |
- | inline policy |
- | aws-managed predefined policy |
- | customer-managed defined policy |
-'''
-import json
-import datetime
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::IAM::User'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
-
- user_name = configuration_item['configuration']['userName']
- iam_client = get_client('iam', event)
-
- # Inline policies
- inline_policy_names = get_all_user_inline_policy_names(iam_client, user_name)
- for policy_name in inline_policy_names:
- policy_document = iam_client.get_user_policy(UserName=user_name, PolicyName=policy_name)['PolicyDocument']
- if is_statements_include_full_star_allow(policy_document['Statement']):
- return build_evaluation_from_config_item(configuration_item, "NON_COMPLIANT", annotation='The inline policy "' + policy_name + '" attached to the user "' + user_name + '" has full star allow permissions.')
-
- # Managed policies
- managed_policy_arn_and_name = get_all_user_managed_policy_arn_and_name(iam_client, user_name)
- for policy_arn, policy_name in managed_policy_arn_and_name.items():
- get_policy = iam_client.get_policy(PolicyArn=policy_arn)
- version = get_policy['Policy']['DefaultVersionId']
- get_policy_version = iam_client.get_policy_version(PolicyArn=policy_arn, VersionId=version)
- if is_statements_include_full_star_allow(get_policy_version['PolicyVersion']['Document']['Statement']):
- return build_evaluation_from_config_item(configuration_item, "NON_COMPLIANT", annotation='The managed policy "' + policy_name + '" attached to the user "' + user_name + '" has full star allow permissions.')
-
- return "COMPLIANT"
-
-def get_all_user_inline_policy_names(iam_client, user_name):
- all_user_inline_policies = []
- list_policy_names = iam_client.list_user_policies(UserName=user_name, MaxItems=1000)
- while True:
- all_user_inline_policies += list_policy_names['PolicyNames']
- if 'Marker' in list_policy_names:
- list_policy_names = iam_client.list_user_policies(UserName=user_name, MaxItems=1000, Marker=list_policy_names['Marker'])
- else:
- break
- return all_user_inline_policies
-
-def get_all_user_managed_policy_arn_and_name(iam_client, user_name):
- all_user_managed_policies_arn_and_name = {}
- list_policy_arn = iam_client.list_attached_user_policies(UserName=user_name, MaxItems=1000)
- while True:
- for policy_dict in list_policy_arn['AttachedPolicies']:
- all_user_managed_policies_arn_and_name[policy_dict['PolicyArn']] = policy_dict['PolicyName']
- if 'Marker' in list_policy_arn:
- list_policy_arn = iam_client.list_attached_user_policies(UserName=user_name, MaxItems=1000, Marker=list_policy_arn['Marker'])
- else:
- break
- return all_user_managed_policies_arn_and_name
-
-def is_statements_include_full_star_allow(statements):
- statement_list = []
- if isinstance(statements, dict):
- statement_list = [statements]
- elif isinstance(statements, list):
- statement_list = statements
- else:
- print("Not recognized statement type:")
- print(statements)
- return False
-
- for statement in statement_list:
- if statement['Effect'] == 'Deny':
- continue
-
- if 'Action' not in statement:
- print("No 'Action' in statement")
- print(statement)
- continue
-
- if isinstance(statement['Action'], list):
- for action in statement['Action']:
- if action == "*":
- return True
- else:
- if statement['Action'] == "*":
- return True
- return False
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Parameter value is invalid",
- internalErrorDetails="An ValueError was raised during the validation of the Parameter value",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluations, ResultToken=resultToken, TestMode=testMode)
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_NO_POLICY_FULL_STAR/IAM_USER_NO_POLICY_FULL_STAR_test.py b/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_NO_POLICY_FULL_STAR/IAM_USER_NO_POLICY_FULL_STAR_test.py
deleted file mode 100644
index a05187d..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_NO_POLICY_FULL_STAR/IAM_USER_NO_POLICY_FULL_STAR_test.py
+++ /dev/null
@@ -1,196 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::IAM::User'
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-iam_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'sts':
- return sts_client_mock
- elif client_name == 'iam':
- return iam_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('IAM_USER_NO_POLICY_FULL_STAR')
-
-class ComplianceTest(unittest.TestCase):
-
- invoking_event = '{"configurationItemDiff":"SomeDifference", "notificationCreationTime":"SomeTime", "messageType":"ConfigurationItemChangeNotification", "recordVersion":"SomeVersion", "configurationItem":{ "resourceType":"AWS::IAM::User","configurationItemStatus":"ResourceDiscovered", "resourceId":"AIDAICVB3PKAQMPEGDW2C", "configurationItemCaptureTime":"2018-02-20T06:56:55.533Z", "configuration":{"userName": "someusername"}}}'
-
- list_user_policy_names = {'PolicyNames': ['policyname1', 'policyname2']}
- get_user_policy_doc = {'PolicyDocument': {"Statement": [{"Effect": "Allow", "Action": "*"}]}}
- no_list_user_policy_names = {'PolicyNames': []}
- list_attached_policy_arn = {'AttachedPolicies': [{'PolicyArn': 'arn1', 'PolicyName': 'policyname1'},{'PolicyArn': 'arn2', 'PolicyName': 'policyname1'}]}
- get_policy = {'Policy': {'DefaultVersionId': 'v2'}}
- get_managed_policy_doc_allow = {"PolicyVersion": {"Document": {"Statement": [{"Effect": "Allow", "Action": "*"}]}}}
- get_managed_policy_doc_deny = {"PolicyVersion": {"Document": {"Statement": [{"Effect": "Deny", "Action": "*"}]}}}
-
- def test_non_compliant_inline(self):
- iam_client_mock.list_user_policies = MagicMock(return_value=self.list_user_policy_names)
- iam_client_mock.get_user_policy = MagicMock(return_value=self.get_user_policy_doc)
- lambdaEvent = build_lambda_configurationchange_event(invoking_event=self.invoking_event)
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'AIDAICVB3PKAQMPEGDW2C', annotation='The inline policy "policyname1" attached to the user "someusername" has full star allow permissions.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_non_compliant_managed(self):
- iam_client_mock.list_user_policies = MagicMock(return_value=self.no_list_user_policy_names)
- iam_client_mock.list_attached_user_policies = MagicMock(return_value=self.list_attached_policy_arn)
- iam_client_mock.get_policy = MagicMock(return_value=self.get_policy)
- iam_client_mock.get_policy_version = MagicMock(return_value=self.get_managed_policy_doc_allow)
- lambdaEvent = build_lambda_configurationchange_event(invoking_event=self.invoking_event)
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'AIDAICVB3PKAQMPEGDW2C', annotation='The managed policy "policyname1" attached to the user "someusername" has full star allow permissions.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_compliant_managed(self):
- iam_client_mock.list_user_policies = MagicMock(return_value=self.no_list_user_policy_names)
- iam_client_mock.list_attached_user_policies = MagicMock(return_value=self.list_attached_policy_arn)
- iam_client_mock.get_policy = MagicMock(return_value=self.get_policy)
- iam_client_mock.get_policy_version = MagicMock(return_value=self.get_managed_policy_doc_deny)
- lambdaEvent = build_lambda_configurationchange_event(invoking_event=self.invoking_event)
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'AIDAICVB3PKAQMPEGDW2C'))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(testClass, response, customerErrorCode=None, customerErrorMessage=None):
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- sts_client_mock.reset_mock(return_value=True)
- sts_client_mock.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_NO_POLICY_FULL_STAR/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_NO_POLICY_FULL_STAR/parameters.json
deleted file mode 100644
index 8f53b8f..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_NO_POLICY_FULL_STAR/parameters.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "IAM_USER_NO_POLICY_FULL_STAR",
- "SourceRuntime": "python3.6",
- "CodeKey": "IAM_USER_NO_POLICY_FULL_STAR.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::IAM::User",
- "RuleSets": [
- "baseline",
- "rulecriticity:high",
- "pci",
- "pci:7.1",
- "iam"
- ]
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_PERMISSION_BOUNDARY_CHECK/IAM_USER_PERMISSION_BOUNDARY_CHECK.py b/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_PERMISSION_BOUNDARY_CHECK/IAM_USER_PERMISSION_BOUNDARY_CHECK.py
deleted file mode 100644
index 8627954..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_PERMISSION_BOUNDARY_CHECK/IAM_USER_PERMISSION_BOUNDARY_CHECK.py
+++ /dev/null
@@ -1,464 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-"""
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- IAM_USER_PERMISSION_BOUNDARY_CHECK
-
-Description:
- Check if all the IAM users have permission boundary attached. The rule is NON_COMPLAINT if the permission boundary is not attached to the IAM user.
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::IAM::User
-
-Rule Parameters:
- policyArns (Optional)
- Comma-separated list of permission boundary policy ARNs, that are expected to be attached to the IAM Users
-
-Scenarios:
-
- Scenario 1:
- Given: Rule parameter policyArns provided
- And: Not Valid one
- Then: Return ERROR
-
- Scenario 2:
- Given: No IAM users in Account
- Then: Return NOT_APPLICABLE
-
- Scenario 3:
- Given: At least 1 IAM User is present in the AWS Account
- And: No permission boundary policy in the Account
- Then: Return NON_COMPLAINT
-
- Scenario 4:
- Given: At least 1 IAM User is present in the AWS Account
- And: Permission boundary policies present in Account
- And: IAM user does not have permission boundary attached
- Then: Return NON_COMPLAINT
-
- Scenario 5:
- Given: At least 1 IAM User is present in the AWS Account
- And: Permission boundary policies present in Account
- And: IAM user does have permission boundary attached
- Then: Return COMPLAINT
-
- Scenario 6:
- Given: Valid Rule parameter policyArns provided
- And: IAM users present in Account
- And: IAM user does have permission boundary attached
- And: The Permission Boundary attached to user is the one listed in parameter.
- Then: Return COMPLAINT
-
- Scenario 7:
- Given: Valid Rule parameter policyArns provided
- And: IAM users present in Account
- And: IAM user does not have permission boundary attached
- Then: Return NON_COMPLAINT
-
- Scenario 8:
- Given: Valid Rule parameter policyArns provided
- And: IAM users present in Account
- And: IAM user does have permission boundary attached
- And: The Permission Boundary attached to user is not the one listed in parameter.
- Then: Return NON_COMPLAINT
-
-"""
-
-import json
-import sys
-import datetime
-import re
-import boto3
-import botocore
-
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::IAM::User'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
-
- iam_client = get_client('iam', event)
- evaluations = []
- users_list = get_all_iam_users(iam_client)
- if not users_list:
- return None
- #To check if atleast 1 permission boundary exists.
- permission_boundary_list = iam_client.list_policies(OnlyAttached=True, PolicyUsageFilter='PermissionsBoundary')
- if not permission_boundary_list:
- for user in users_list:
- return evaluations.append(build_evaluation(user['UserId'], 'NON_COMPLIANT', event, annotation='No permission boundary is attached to this IAM User.'))
- for user in users_list:
- compliance_type = evaluate_user(user['UserName'], valid_rule_parameters, iam_client)
- evaluations.append(build_evaluation(user['UserId'], compliance_type, event))
- return evaluations
-
-def get_all_iam_users(client):
- list_to_return = []
- user_list = client.list_users()
- while True:
- for user in user_list['Users']:
- list_to_return.append(user)
- if 'Marker' in user_list:
- user_list = client.list_users(Marker=user_list['Marker'])
- else:
- return list_to_return
-#This function checks the IAM user for permission boundary policy and declares COMPLAINT and NON_COMPLAINT accordingly.
-def evaluate_user(username, valid_rule_parameters, iam_client):
- user_details = iam_client.get_user(UserName=username)
- if not 'PermissionsBoundary' in user_details['User']:
- return 'NON_COMPLIANT'
- if not 'policyArns' in valid_rule_parameters:
- return 'COMPLIANT'
- boundary_name = user_details['User']['PermissionsBoundary']['PermissionsBoundaryArn']
- for permission_policy_name in valid_rule_parameters['policyArns']:
- if permission_policy_name == boundary_name:
- return 'COMPLIANT'
- return 'NON_COMPLIANT'
-
-def evaluate_parameters(rule_parameters):
- if rule_parameters:
- boundary_policy_names = rule_parameters['policyArns'].replace(" ", "")
- boundary_policy_name_list = boundary_policy_names.split(",")
- for permission_policy_name in boundary_policy_name_list:
- if not re.match(r'^arn:aws:iam::(\d{12}|aws):policy/.{1,128}', permission_policy_name):
- raise ValueError('The parameter should be a valid ARN format of the policy')
- if len(permission_policy_name) > 161:
- raise ValueError('The permission boundary policy name is greater than 128 characters')
-
- rule_parameters['policyArns'] = boundary_policy_name_list
-
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_PERMISSION_BOUNDARY_CHECK/IAM_USER_PERMISSION_BOUNDARY_CHECK_test.py b/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_PERMISSION_BOUNDARY_CHECK/IAM_USER_PERMISSION_BOUNDARY_CHECK_test.py
deleted file mode 100644
index 53a98a4..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_PERMISSION_BOUNDARY_CHECK/IAM_USER_PERMISSION_BOUNDARY_CHECK_test.py
+++ /dev/null
@@ -1,247 +0,0 @@
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- import mock
- from mock import MagicMock
-import botocore
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::IAM::User'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-IAM_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- elif client_name == 'sts':
- return STS_CLIENT_MOCK
- elif client_name == 'iam':
- return IAM_CLIENT_MOCK
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('IAM_USER_PERMISSION_BOUNDARY_CHECK')
-
-class TESTInvalidpermissionboundary(unittest.TestCase):
- user_list = {'Users': [{'UserId': 'AIDAIDFOUX2OSRO6DO7XM', 'UserName': 'user-name-1'}, {'UserId': 'AIDAIDFOUX2OSRO6DO7XN', 'UserName': 'user-name-2'}]}
-
- # Premission Boundary Name is provided as the input but policy name is in invalid format.
-
- def test_scenario1(self):
- IAM_CLIENT_MOCK.list_users = MagicMock(return_value=self.user_list)
- IAM_CLIENT_MOCK.get_user = MagicMock(return_value={'User':{}})
- rule_param = "{\"policyArns\":\"arn:aws:iam::aws:/AdministratorAccess\"}"
- lambda_event = build_lambda_scheduled_event(rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException', customer_error_message='The parameter should be a valid ARN format of the policy')
-
-class TESTScenarios2to8(unittest.TestCase):
- user_list = {'Users': [{'UserId': 'AIDAIDFOUX2OSRO6DO7XM', 'UserName': 'user-name-1'}, {'UserId': 'AIDAIDFOUX2OSRO6DO7XN', 'UserName': 'user-name-2'}]}
-
- def construct_permission_list(self, UserName):
- user_info_list = {"User": {"UserName": "user-name-1", "PermissionsBoundary": {"PermissionsBoundaryType": "Policy", "PermissionsBoundaryArn": "arn:aws:iam::aws:policy/AdministratorAccess"}, "UserId": "AIDAJDTYJDDPUWJ7IR3G2", "Arn": "arn:aws:iam::677885075477:user/ddbtest"}}
- return user_info_list
-
- # No IAM users present in the account.
- def test_scenario2(self):
- IAM_CLIENT_MOCK.list_users = MagicMock(return_value={"Users":[]})
- lambda_event = build_lambda_scheduled_event()
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected, 1)
-
- # Atleast 1 IAM user present in the account but No Permission policies in the account
- def test_scenario3(self):
- IAM_CLIENT_MOCK.list_users = MagicMock(return_value=self.user_list)
- IAM_CLIENT_MOCK.list_policies = MagicMock(return_value={"Policies": []})
- lambda_event = build_lambda_scheduled_event()
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'AIDAIDFOUX2OSRO6DO7XM'))
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'AIDAIDFOUX2OSRO6DO7XN'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- # Permission Boundary is present in the Account but IAM user does not have it attached.
- def test_scenario4(self):
- IAM_CLIENT_MOCK.list_users = MagicMock(return_value=self.user_list)
- IAM_CLIENT_MOCK.get_user = MagicMock(return_value={'User':{}})
- lambda_event = build_lambda_scheduled_event()
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'AIDAIDFOUX2OSRO6DO7XM'))
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'AIDAIDFOUX2OSRO6DO7XN'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- # Permission Boundary is present in the Account and IAM user does have it attached.
- def test_scenario5(self):
- IAM_CLIENT_MOCK.list_users = MagicMock(return_value=self.user_list)
- IAM_CLIENT_MOCK.get_user = MagicMock(side_effect=self.construct_permission_list)
- lambda_event = build_lambda_scheduled_event()
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'AIDAIDFOUX2OSRO6DO7XM'))
- resp_expected.append(build_expected_response('COMPLIANT', 'AIDAIDFOUX2OSRO6DO7XN'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- # Permission Boundary Name is provided as the input and IAM user does have it attached.
- def test_scenario6(self):
- IAM_CLIENT_MOCK.list_users = MagicMock(return_value=self.user_list)
- IAM_CLIENT_MOCK.get_user = MagicMock(side_effect=self.construct_permission_list)
- rule_param = "{\"policyArns\":\"arn:aws:iam::aws:policy/AdministratorAccess\"}"
- lambda_event = build_lambda_scheduled_event(rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'AIDAIDFOUX2OSRO6DO7XM'))
- resp_expected.append(build_expected_response('COMPLIANT', 'AIDAIDFOUX2OSRO6DO7XN'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- # Permission Boundary Name is provided as the input and IAM user does not have any permission boundary attached.
- def test_scenario7(self):
- IAM_CLIENT_MOCK.list_users = MagicMock(return_value=self.user_list)
- IAM_CLIENT_MOCK.get_user = MagicMock(return_value={'User':{}})
- IAM_CLIENT_MOCK.get_user = MagicMock(return_value={'User':{}})
- rule_param = "{\"policyArns\":\"arn:aws:iam::aws:policy/AdministratorAccess\"}"
- lambda_event = build_lambda_scheduled_event(rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'AIDAIDFOUX2OSRO6DO7XM'))
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'AIDAIDFOUX2OSRO6DO7XN'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- # Permission Boundary Name is provided as the input but IAM user does not have it attached.
- def test_scenario8(self):
- IAM_CLIENT_MOCK.list_users = MagicMock(return_value=self.user_list)
- IAM_CLIENT_MOCK.get_user = MagicMock(return_value={"User": {"UserName": "user-name-2", "PermissionsBoundary": {"PermissionsBoundaryType": "Policy", "PermissionsBoundaryArn": "arn:aws:iam::aws:policy/AdminAccess"}, "UserId": "AIDAIDFOUX2OSRO6DO7XN", "Arn": "arn:aws:iam::677885075477:user/ddbtest"}})
- IAM_CLIENT_MOCK.get_user = MagicMock(return_value={"User": {"UserName": "user-name-1", "PermissionsBoundary": {"PermissionsBoundaryType": "Policy", "PermissionsBoundaryArn": "arn:aws:iam::aws:policy/AAccess"}, "UserId": "AIDAIDFOUX2OSRO6DO7XN", "Arn": "arn:aws:iam::677885075477:user/ddbtest"}})
- rule_param = "{\"policyArns\":\"arn:aws:iam::aws:policy/AdministratorAccess\"}"
- lambda_event = build_lambda_scheduled_event(rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'AIDAIDFOUX2OSRO6DO7XM'))
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'AIDAIDFOUX2OSRO6DO7XN'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_PERMISSION_BOUNDARY_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_PERMISSION_BOUNDARY_CHECK/parameters.json
deleted file mode 100644
index eb8bd28..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_PERMISSION_BOUNDARY_CHECK/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "CodeKey": "IAM_USER_PERMISSION_BOUNDARY_CHECK.zip",
- "SourceRuntime": "python3.6",
- "SourcePeriodic": "TwentyFour_Hours",
- "RuleName": "IAM_USER_PERMISSION_BOUNDARY_CHECK",
- "OptionalParameters": "{\"policyArns\":\"arn:aws:iam::aws:policy/AdministratorAccess\"}",
- "InputParameters": "{}"
- },
- "Tags": "[]"
-}
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_UNUSED_CREDENTIALS_CHECK/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_UNUSED_CREDENTIALS_CHECK/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_UNUSED_CREDENTIALS_CHECK/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_UNUSED_CREDENTIALS_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_UNUSED_CREDENTIALS_CHECK/parameters.json
deleted file mode 100644
index 773976e..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/IAM_USER_UNUSED_CREDENTIALS_CHECK/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "IAM_USER_UNUSED_CREDENTIALS_CHECK",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{\"maxCredentialUsageAge\": \"90\"}",
- "OptionalParameters": "{}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "IAM_USER_UNUSED_CREDENTIALS_CHECK"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/INSTANCE_PROFILE_HAVE_DEFINED_POLICIES/INSTANCE_PROFILE_HAVE_DEFINED_POLICIES.py b/vendor/github.com/awslabs/aws-config-rules/python/INSTANCE_PROFILE_HAVE_DEFINED_POLICIES/INSTANCE_PROFILE_HAVE_DEFINED_POLICIES.py
deleted file mode 100644
index 64c082a..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/INSTANCE_PROFILE_HAVE_DEFINED_POLICIES/INSTANCE_PROFILE_HAVE_DEFINED_POLICIES.py
+++ /dev/null
@@ -1,389 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-'''
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- instance-profile-have-defined-policies
-
-Description:
- Checks that the correct policies are attached to particular EC2 instance profiles.
-
-Trigger:
- Configuration Changes on AWS::IAM::Role
-
-Resource Type to report on:
- AWS::IAM::Role
-
-Rule Parameters:
- None
-
-Feature:
- In order to: ensure the proper permissions for logging, backup and others,
- As: a Security Officer
- I want: To ensure that Instance Profile policies match our expectations.
-
-Scenarios:
-
- Scenario 1:
- Given: An IAM role name which does not includes NAME_ROLE_TO_CHECK.
- Then: Return NOT_APPLICABLE
-
- Scenario 2:
- Given: An IAM role name which includes NAME_ROLE_TO_CHECK.
- And: At least one policy in POLICY_NAMES_THAT_MUST_BE_ATTACHED is not attached.
- Then: Return NON_COMPLIANT
-
- Scenario 3:
- Given: An IAM role name which includes NAME_ROLE_TO_CHECK.
- And: All policies in POLICY_NAMES_THAT_MUST_BE_ATTACHED are attached.
- Then: Return COMPLIANT
-
-'''
-
-import json
-import datetime
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-NAME_ROLE_TO_CHECK = '.db.ec2role.iaminstancerole'
-POLICY_NAMES_THAT_MUST_BE_ATTACHED = ['AmazonEC2RoleforSSM', 'CloudWatchAgentServerPolicy', 'EC2InstanceDescribe', 'DBBackupBucket']
-DEFAULT_RESOURCE_TYPE = 'AWS::IAM::Role'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
-
- resource_name = configuration_item["resourceName"]
-
- if resource_name.find(NAME_ROLE_TO_CHECK) == -1:
- return 'NOT_APPLICABLE'
-
- policy_counter = 0
- for policy in configuration_item['configuration']['attachedManagedPolicies']:
- if policy['policyName'] in POLICY_NAMES_THAT_MUST_BE_ATTACHED:
- policy_counter += 1
- if policy_counter == len(POLICY_NAMES_THAT_MUST_BE_ATTACHED):
- return 'COMPLIANT'
-
- return build_evaluation_from_config_item(configuration_item, 'NON_COMPLIANT', annotation="One or more mandatory policy is not attached to this instance profile.")
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
- Return:
- anything suitable for the evaluate_compliance()
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Customer error while parsing input parameters",
- internalErrorDetails="Parameter value is invalid",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
- AWS_CONFIG_CLIENT = get_client('config', event)
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- configuration_item = get_configuration_item(invoking_event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return {'internalErrorMessage': 'Unexpected message type ' + str(invoking_event)}
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluations, ResultToken=resultToken, TestMode=testMode)
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/INSTANCE_PROFILE_HAVE_DEFINED_POLICIES/INSTANCE_PROFILE_HAVE_DEFINED_POLICIES_test.py b/vendor/github.com/awslabs/aws-config-rules/python/INSTANCE_PROFILE_HAVE_DEFINED_POLICIES/INSTANCE_PROFILE_HAVE_DEFINED_POLICIES_test.py
deleted file mode 100644
index e3d234f..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/INSTANCE_PROFILE_HAVE_DEFINED_POLICIES/INSTANCE_PROFILE_HAVE_DEFINED_POLICIES_test.py
+++ /dev/null
@@ -1,170 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-import sys
-import unittest
-import json
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::IAM::Role'
-POLICY_NAMES_THAT_MUST_BE_ATTACHED = ['AmazonEC2RoleforSSM', 'CloudWatchAgentServerPolicy', 'EC2InstanceDescribe', 'DBBackupBucket']
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-iam_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- if client_name == 'iam':
- return iam_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('INSTANCE_PROFILE_HAVE_DEFINED_POLICIES')
-
-class SampleTest(unittest.TestCase):
-
- def test_Scenario_1_IF_APPLICABLE_1(self):
- attachedManagedPolicies = populate_attachedManagedPolicies(4)
- resourceName = "test1.db.ec2role.test1"
- response = rule.lambda_handler(build_lambda_configurationchange_event(build_invoking_event(resourceName, attachedManagedPolicies)), {})
- # print (response)
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', 'some-resource-id', 'AWS::IAM::Role'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_1_IF_APPLICABLE_2(self):
- attachedManagedPolicies = populate_attachedManagedPolicies(2)
- resourceName = "test1.ec2role.iaminstancerole.test1"
- response = rule.lambda_handler(build_lambda_configurationchange_event(build_invoking_event(resourceName, attachedManagedPolicies)), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', 'some-resource-id', 'AWS::IAM::Role'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_2_NOT_INSTANCE_PROFILE_1(self):
- attachedManagedPolicies = []
- resourceName = "test1.db.ec2role.iaminstancerole.test1"
- response = rule.lambda_handler(build_lambda_configurationchange_event(build_invoking_event(resourceName, attachedManagedPolicies)), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'some-resource-id', 'AWS::IAM::Role', annotation='One or more mandatory policy is not attached to this instance profile.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_3_IS_INSTANCE_PROFILE(self):
- attachedManagedPolicies = populate_attachedManagedPolicies(4)
- resourceName = "test1.db.ec2role.iaminstancerole.test1"
- response = rule.lambda_handler(build_lambda_configurationchange_event(build_invoking_event(resourceName, attachedManagedPolicies)), {})
- # print (response)
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'some-resource-id', 'AWS::IAM::Role'))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-def build_invoking_event(resourceName, attachedManagedPolicies):
- invoking_event = {
- "messageType":"ConfigurationItemChangeNotification",
- "configurationItem":{
- "resourceType":"AWS::IAM::Role",
- "resourceId": "some-resource-id",
- "configurationItemStatus": "OK",
- "configurationItemCaptureTime": "anytime",
- "resourceName": resourceName,
- "configuration":{
- "attachedManagedPolicies": attachedManagedPolicies
- }
- }
- }
- return json.dumps(invoking_event)
-
-def populate_attachedManagedPolicies(i):
- attachedManagedPolicies = []
- for policy in POLICY_NAMES_THAT_MUST_BE_ATTACHED[:i]:
- attachedManagedPolicies.append({
- "policyName": policy
- })
- return attachedManagedPolicies
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'INSTANCE_PROFILE_HAVE_DEFINED_POLICIES',
- 'executionRoleArn':'arn:aws:config:ap-south-1:633141505637:config-rule/config-rule-m1ypfw',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '633141505637',
- 'configRuleArn': 'arn:aws:config:ap-south-1:633141505637:config-rule/config-rule-m1ypfw',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': True,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- print (response_expected)
- print (response[i])
- if 'Annotation' in response_expected and 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/INSTANCE_PROFILE_HAVE_DEFINED_POLICIES/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/INSTANCE_PROFILE_HAVE_DEFINED_POLICIES/parameters.json
deleted file mode 100644
index 8a79831..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/INSTANCE_PROFILE_HAVE_DEFINED_POLICIES/parameters.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "Parameters": {
- "RuleName": "INSTANCE_PROFILE_HAVE_DEFINED_POLICIES",
- "SourceRuntime": "python3.6",
- "CodeKey": "INSTANCE_PROFILE_HAVE_DEFINED_POLICIES.zip",
- "InputParameters": "{}",
- "SourceEvents": "AWS::IAM::Role"
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/INTERNET_GATEWAY_AUTHORIZED_ONLY/INTERNET_GATEWAY_AUTHORIZED_ONLY.py b/vendor/github.com/awslabs/aws-config-rules/python/INTERNET_GATEWAY_AUTHORIZED_ONLY/INTERNET_GATEWAY_AUTHORIZED_ONLY.py
deleted file mode 100644
index 3ecd29e..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/INTERNET_GATEWAY_AUTHORIZED_ONLY/INTERNET_GATEWAY_AUTHORIZED_ONLY.py
+++ /dev/null
@@ -1,398 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-'''
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- internet-gateway-authorized-only
-
-Description:
- Check whether attached IGWs are attached to an authorized list of VPCs.
-
-Trigger:
- Configuration Change on AWS::EC2::InternetGateway
-
-Reports on:
- AWS::EC2::InternetGateway
-
-Parameters:
- | ----------------------|-----------|-------------------------------------------------|
- | Parameter Name | Type | Description |
- | ----------------------|-----------|-------------------------------------------------|
- | AuthorizedVpcIds | Optional | List of the authorized VPC Ids to have an IGW |
- | | | attached, separated by comma (,). |
- | ----------------------|-----------|-------------------------------------------------|
-
-Feature:
- In order to: limit access to the internet to specific VPCs
- As: a Security Officer
- I want: to ensure that all attached IGWs are authorized.
-
-Scenarios:
- Scenario 1:
- Given: the AuthorizedVpcIds list items are not starting with "vpc-"
- Then: return an Error
-
- Scenario 2:
- Given: the IGW is not attached to a VPC
- Then: return COMPLIANT
-
- Scenario 3:
- Given: the IGW is attached to a VPC
- And: the AuthorizedVpcIds parameter is not configured
- Then: return NON_COMPLIANT
-
- Scenario 4:
- Given: the IGW is attached to a VPC
- And: the AuthorizedVpcIds parameter is configured and valid
- And: the VPC Id where the IGW is attached is not in the AuthorizedVpcIds list
- Then: return NON_COMPLIANT with an annotation "This IGW is not attached to an authorized VPC."
-
- Scenario 5:
- Given: the IGW is attached to a VPC
- And: the AuthorizedVpcIds parameter is configured and valid
- And: the VPC Id where the IGW is attached is in the AuthorizedVpcIds list
- Then: return COMPLIANT
-'''
-
-import json
-import datetime
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = "AWS::EC2::InternetGateway"
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
-
- vpc_attachment = configuration_item['configuration']['attachments']
- if not vpc_attachment:
- return 'COMPLIANT'
-
- vpc_id = vpc_attachment[0]['vpcId']
- if vpc_id in valid_rule_parameters:
- return 'COMPLIANT'
- return build_evaluation_from_config_item(configuration_item, 'NON_COMPLIANT', annotation="This IGW is not attached to an authorized VPC.")
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- if rule_parameters:
- authorized_vpc_ids = rule_parameters['AuthorizedVpcIds'].split(',')
- for i,authorized_vpc_id in enumerate(authorized_vpc_ids):
- authorized_vpc_ids[i] = authorized_vpc_id.strip()
- for authorized_vpc_id in authorized_vpc_ids:
- if not authorized_vpc_id.startswith('vpc-'):
- raise ValueError('The parameter ({}) does not start with vpc-'.format(authorized_vpc_id))
- rule_parameters = authorized_vpc_ids
- return rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Parameter value is invalid",
- internalErrorDetails="An ValueError was raised during the validation of the Parameter value",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluations, ResultToken=resultToken, TestMode=testMode)
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/INTERNET_GATEWAY_AUTHORIZED_ONLY/INTERNET_GATEWAY_AUTHORIZED_ONLY_test.py b/vendor/github.com/awslabs/aws-config-rules/python/INTERNET_GATEWAY_AUTHORIZED_ONLY/INTERNET_GATEWAY_AUTHORIZED_ONLY_test.py
deleted file mode 100644
index e517781..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/INTERNET_GATEWAY_AUTHORIZED_ONLY/INTERNET_GATEWAY_AUTHORIZED_ONLY_test.py
+++ /dev/null
@@ -1,202 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-import json
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = "AWS::EC2::InternetGateway"
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'sts':
- return sts_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('INTERNET_GATEWAY_AUTHORIZED_ONLY')
-
-class SampleTest(unittest.TestCase):
- def setUp(self):
- pass
-
- def test_Scenario_1_not_starting_with_vpc(self):
- rule_parameters = "{\"AuthorizedVpcIds\":\"somename, vpc-shruti\"}"
- vpc_id_igw = 'vpc-abcde'
- response = rule.lambda_handler(build_lambda_configurationchange_event(build_invoking_event(vpc_id_igw), rule_parameters), {})
- assert_customer_error_response(self, response, customerErrorCode='InvalidParameterValueException', customerErrorMessage='The parameter (somename) does not start with vpc-')
-
- def test_Scenario_2_igw_not_attached_vpc(self):
- rule_parameters = "{\"AuthorizedVpcIds\":\"vpc-paranshu, vpc-shruti\"}"
- vpc_id_igw = ""
- response = rule.lambda_handler(build_lambda_configurationchange_event(build_invoking_event(vpc_id_igw), rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'some-resource-id'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_3_AuthorizedVpcIds_not_configured(self):
- rule_parameters = ""
- vpc_id_igw = 'vpc-abcde'
- response = rule.lambda_handler(build_lambda_configurationchange_event(build_invoking_event(vpc_id_igw), rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'some-resource-id', annotation='This IGW is not attached to an authorized VPC.'))
- assert_successful_evaluation(self, response, resp_expected)
-
-
- def test_scenario_4_igw_no_authorized_vpc(self):
- rule_parameters = "{\"AuthorizedVpcIds\":\"vpc-paranshu, vpc-shruti\"}"
- vpc_id_igw = 'vpc-abcde'
- response = rule.lambda_handler(build_lambda_configurationchange_event(build_invoking_event(vpc_id_igw), rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'some-resource-id', annotation='This IGW is not attached to an authorized VPC.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_Scenario_5_compliant(self):
- rule_parameters = "{\"AuthorizedVpcIds\":\"vpc-paranshu, vpc-shruti\"}"
- vpc_id_igw = 'vpc-shruti'
- response = rule.lambda_handler(build_lambda_configurationchange_event(build_invoking_event(vpc_id_igw), rule_parameters), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'some-resource-id'))
- assert_successful_evaluation(self, response, resp_expected)
-
-def build_invoking_event(invoking_event_igw):
- attachments = []
- if(len(invoking_event_igw)>0):
- attachments.append({
- "vpcId": invoking_event_igw,
- "state": "available"
- })
- invoking_event_iam_role_sample = {
- "configurationItem": {
- "relatedEvents": [],
- "relationships": [],
- "configuration": {
- "internetGatewayId": "igw-a5f227c1",
- "attachments": attachments,
- "tags": []
- },
- "tags": {},
- "configurationItemCaptureTime": "2018-07-02T03:37:52.418Z",
- "awsAccountId": "633141505637",
- "configurationItemStatus": "ResourceDiscovered",
- "resourceType": "AWS::EC2::InternetGateway",
- "resourceId": "some-resource-id",
- "resourceName": "some-resource-name",
- "ARN": "some-arn"
- },
- "notificationCreationTime": "2018-07-02T23:05:34.445Z",
- "messageType": "ConfigurationItemChangeNotification"
- }
- return json.dumps(invoking_event_iam_role_sample)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(testClass, response, customerErrorCode=None, customerErrorMessage=None):
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- sts_client_mock.reset_mock(return_value=True)
- sts_client_mock.assume_role = MagicMock(return_value=assume_role_response)
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/INTERNET_GATEWAY_AUTHORIZED_ONLY/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/INTERNET_GATEWAY_AUTHORIZED_ONLY/parameters.json
deleted file mode 100644
index 2d7a1fb..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/INTERNET_GATEWAY_AUTHORIZED_ONLY/parameters.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "INTERNET_GATEWAY_AUTHORIZED_ONLY",
- "SourceRuntime": "python3.6",
- "CodeKey": "INTERNET_GATEWAY_AUTHORIZED_ONLY.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{\"AuthorizedVpcIds\":\"\"}",
- "SourceEvents": "AWS::EC2::InternetGateway",
- "RuleSets": [
- "accountclassification:secret",
- "accountclassification:confidential",
- "rulecriticity:high",
- "pci"
- ]
- }
-}
-
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/KMS_KEYS_TO_NOT_DELETE/KMS_KEYS_TO_NOT_DELETE.py b/vendor/github.com/awslabs/aws-config-rules/python/KMS_KEYS_TO_NOT_DELETE/KMS_KEYS_TO_NOT_DELETE.py
deleted file mode 100644
index b39cc31..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/KMS_KEYS_TO_NOT_DELETE/KMS_KEYS_TO_NOT_DELETE.py
+++ /dev/null
@@ -1,454 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-'''
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- KMS_KEYS_TO_NOT_DELETE
-
-Description:
- Check that Customer Managed keys are not scheduled for deletion. The rule is NON_COMPLAINT if the Customer Managed Keys are scheduled for deletion. This rule does not include AWS-managed and Imported Keys.
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::KMS::Key
-
-Rule Parameters:
- kmsKeyIds (Optional)
- Comma-separated list of specific Customer Managed Key Ids, that are expected not to be scheduled for deletion.
-
-Scenarios:
- Scenario 1:
- Given: Rule parameter kmsKeyIds are configured and not valid
- Then: Return ERROR
-
- Scenario 2:
- Given: No CMKs present
- Then: Return NOT_APPLICABLE
-
- Scenario 3:
- Given: At least 1 CMK is present
- And: Rule parameter kmsKeyIds are not configured
- And: The KMS Key is not scheduled for deletion
- Then: Return COMPLIANT
-
- Scenario 4:
- Given: At least 1 CMK is present
- And: Rule parameter kmsKeyIds are not configured
- And: The KMS Key in the account is scheduled for deletion
- Then: Return NON_COMPLIANT
-
- Scenario 5:
- Given: At least 1 CMK is present
- And: Rule parameter kmsKeyIds are configured and valid
- And: The KMS key in the parameter is not an existing kms key
- Then: Return NOT_APPLICABLE
-
- Scenario 6:
- Given: At least 1 CMK is present
- And: Rule parameter kmsKeyIds are configured and valid
- And: The CMK is one of the keys in the parameter
- And: The CMK is not scheduled for deletion
- Then: Return COMPLIANT
-
- Scenario 7:
- Given: At least 1 CMK is present
- And: Rule parameter kmsKeyIds are configured and valid
- And: The CMK is one of the keys in the parameter
- And: The CMK is scheduled for deletion
- Then: Return NON_COMPLIANT
-'''
-
-import json
-import sys
-import datetime
-import re
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::KMS::Key'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- evaluations = []
- kms_client = get_client('kms', event)
- all_kms_key_list = get_all_kms_keys(kms_client)
-
- if not all_kms_key_list:
- return None
-
- if 'kmsKeyIds' in valid_rule_parameters:
- for key_id in valid_rule_parameters['kmsKeyIds']:
- if key_id in all_kms_key_list:
- kms_key_details = kms_client.describe_key(KeyId=key_id)
- if kms_key_details['KeyMetadata']['Origin'] == 'AWS_KMS' and kms_key_details['KeyMetadata']['KeyManager'] == 'CUSTOMER':
- if kms_key_details['KeyMetadata']['KeyState'] == 'PendingDeletion':
- evaluations.append(build_evaluation(key_id, 'NON_COMPLIANT', event, annotation='The KMS Key is scheduled for deletion.'))
- continue
- evaluations.append(build_evaluation(key_id, 'COMPLIANT', event))
- else:
- evaluations.append(build_evaluation(key_id, 'NOT_APPLICABLE', event, annotation='The given kmsKeyId does not exist. Please verify the kmsKeyId and try again.'))
-
- return evaluations
-
- for key_id in all_kms_key_list:
- kms_key_details = kms_client.describe_key(KeyId=key_id)
- if kms_key_details['KeyMetadata']['Origin'] == 'AWS_KMS' and kms_key_details['KeyMetadata']['KeyManager'] == 'CUSTOMER':
- if kms_key_details['KeyMetadata']['KeyState'] == 'PendingDeletion':
- evaluations.append(build_evaluation(key_id, 'NON_COMPLIANT', event, annotation='The KMS Key is scheduled for deletion.'))
- continue
- evaluations.append(build_evaluation(key_id, 'COMPLIANT', event))
- return evaluations
-
-def get_all_kms_keys(kms_client):
- all_kms_key_list = []
- response = kms_client.list_keys(Limit=1000)
- while response['Keys']:
- for key in response['Keys']:
- all_kms_key_list.append(key['KeyId'])
- if not 'NextMarker' in response:
- return all_kms_key_list
- response = kms_client.list_keys(Marker=response['NextMarker'], Limit=1000)
-
-def evaluate_parameters(rule_parameters):
- if rule_parameters:
- kms_key_list = rule_parameters['kmsKeyIds'].replace(" ", "")
- kms_key_list = kms_key_list.split(',')
-
- regex_pattern = re.compile("^[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}$")
- for kms_key in kms_key_list:
- if not regex_pattern.match(kms_key):
- raise ValueError('The KMS Key id should be in the right format.')
- rule_parameters['kmsKeyIds'] = kms_key_list
-
- return rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return status in ('OK', 'ResourceDiscovered') and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/KMS_KEYS_TO_NOT_DELETE/KMS_KEYS_TO_NOT_DELETE_test.py b/vendor/github.com/awslabs/aws-config-rules/python/KMS_KEYS_TO_NOT_DELETE/KMS_KEYS_TO_NOT_DELETE_test.py
deleted file mode 100644
index 7ba8993..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/KMS_KEYS_TO_NOT_DELETE/KMS_KEYS_TO_NOT_DELETE_test.py
+++ /dev/null
@@ -1,207 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::KMS::Key'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-KMS_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 'kms':
- return KMS_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('KMS_KEYS_TO_NOT_DELETE')
-
-class ComplianceTest(unittest.TestCase):
-
- kms_key_list = {"Keys": [{"KeyId": "83de41d6-6530-49c1-9cb7-1de1560ce5tg"}]}
-
- def setUp(self):
- pass
-
- def test_scenario_1_invalid_param(self):
- rule_param = "{\"kmsKeyIds\":\"83de41d66530-49c1-9cb7-1de1560ce5tg\"}"
- KMS_CLIENT_MOCK.list_keys = MagicMock(return_value=self.kms_key_list)
- KMS_CLIENT_MOCK.describe_key = MagicMock(return_value={"KeyMetadata":{}})
- lambda_event = build_lambda_scheduled_event(rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException', customer_error_message='The KMS Key id should be in the right format.')
-
- def test_scenario_2_no_keys(self):
- rule_param = {}
- KMS_CLIENT_MOCK.list_keys = MagicMock(return_value={"Keys":[]})
- lambda_event = build_lambda_scheduled_event(rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario_3_no_param(self):
- rule_param = {}
- KMS_CLIENT_MOCK.list_keys = MagicMock(return_value=self.kms_key_list)
- KMS_CLIENT_MOCK.describe_key = MagicMock(return_value={"KeyMetadata": {"KeyState": "Enabled", "Origin": "AWS_KMS", "KeyManager": "CUSTOMER"}})
- lambda_event = build_lambda_scheduled_event(rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', '83de41d6-6530-49c1-9cb7-1de1560ce5tg'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario_4_noncomplaint(self):
- rule_param = {}
- KMS_CLIENT_MOCK.list_keys = MagicMock(return_value=self.kms_key_list)
- KMS_CLIENT_MOCK.describe_key = MagicMock(return_value={"KeyMetadata": {"KeyState": "PendingDeletion", "Origin": "AWS_KMS", "KeyManager": "CUSTOMER"}})
- lambda_event = build_lambda_scheduled_event(rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '83de41d6-6530-49c1-9cb7-1de1560ce5tg', annotation='The KMS Key is scheduled for deletion.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario_5_param_not(self):
- rule_param = "{\"kmsKeyIds\":\"fe727d4a-1bb7-4226-82d8-9d50b9aece5t\"}"
- KMS_CLIENT_MOCK.list_keys = MagicMock(return_value=self.kms_key_list)
- KMS_CLIENT_MOCK.describe_key = MagicMock(return_value={"KeyMetadata": {"KeyState": "PendingDeletion", "Origin": "AWS_KMS", "KeyManager": "CUSTOMER"}})
- lambda_event = build_lambda_scheduled_event(rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', 'fe727d4a-1bb7-4226-82d8-9d50b9aece5t', annotation='The given kmsKeyId does not exist. Please verify the kmsKeyId and try again.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario_6_complaint(self):
- rule_param = "{\"kmsKeyIds\":\"83de41d6-6530-49c1-9cb7-1de1560ce5tg\"}"
- KMS_CLIENT_MOCK.list_keys = MagicMock(return_value=self.kms_key_list)
- KMS_CLIENT_MOCK.describe_key = MagicMock(return_value={"KeyMetadata": {"KeyState": "Enabled", "Origin": "AWS_KMS", "KeyManager": "CUSTOMER"}})
- lambda_event = build_lambda_scheduled_event(rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', '83de41d6-6530-49c1-9cb7-1de1560ce5tg'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario_7_noncomplaint(self):
- rule_param = "{\"kmsKeyIds\":\"83de41d6-6530-49c1-9cb7-1de1560ce5tg\"}"
- KMS_CLIENT_MOCK.list_keys = MagicMock(return_value=self.kms_key_list)
- KMS_CLIENT_MOCK.describe_key = MagicMock(return_value={"KeyMetadata": {"KeyState": "PendingDeletion", "Origin": "AWS_KMS", "KeyManager": "CUSTOMER"}})
- lambda_event = build_lambda_scheduled_event(rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '83de41d6-6530-49c1-9cb7-1de1560ce5tg', annotation='The KMS Key is scheduled for deletion.'))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/KMS_KEYS_TO_NOT_DELETE/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/KMS_KEYS_TO_NOT_DELETE/parameters.json
deleted file mode 100644
index bb540d4..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/KMS_KEYS_TO_NOT_DELETE/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "KMS_KEYS_TO_NOT_DELETE",
- "SourceRuntime": "python3.6",
- "SourcePeriodic": "One_Hour",
- "CodeKey": "KMS_KEYS_TO_NOT_DELETE.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{\"kmsKeyIds\":\"\"}"
- },
- "Tags": "[]"
-}
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_CODE_IS_VERSIONED/LAMBDA_CODE_IS_VERSIONED.py b/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_CODE_IS_VERSIONED/LAMBDA_CODE_IS_VERSIONED.py
deleted file mode 100644
index 4612555..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_CODE_IS_VERSIONED/LAMBDA_CODE_IS_VERSIONED.py
+++ /dev/null
@@ -1,380 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-'''
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- lambda-code-is-versioned
-
-Description:
- Ensure that all the lambda functions have at least one defined version and alias, also ensure that no alias pointing to $LATEST version
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::Lambda::Function
-
-Feature:
- In order to: Avoid misconfigurations of lambda functions
- As: A security officer
- I want: To ensure that all lambda functions have at least one version and alias points to a version and not the $LATEST.
-
-Scenarios:
- Scenario 1:
- Given: no lambda function present
- Then: return NOT_APPLICABLE
-
- Scenario 2:
- Given: Lambda function is present
- And: Lambda function does not have at least one version
- Then: return NON_COMPLIANT
-
- Scenario 3:
- Given: Lambda function is present
- And: Lambda function has at least one version
- And: no alias present for lambda function.
- Then: return NON_COMPLIANT
-
- Scenario 4:
- Given: Lambda function is present
- And: Lambda function has at least one version
- And: at least one alias is present for a lambda function
- And: alias points to $LATEST version
- Then: return NON_COMPLIANT
-
- Scenario 5:
- Given: Lambda function is present
- And: Lambda function has at least one version
- And: at least one alias is present for a lambda function
- And: no alias points to $LATEST version
- Then: return COMPLIANT
-'''
-
-import json
-import datetime
-import boto3
-import botocore
-
-DEFAULT_RESOURCE_TYPE = "AWS::Lambda::Function"
-ASSUME_ROLE_MODE = False
-
-def evaluate_compliance(event, configuration_item, rule_parameters):
-
- lambda_client = get_client('lambda', event)
-
- function_name_list = list_all_lambda_function_names(lambda_client)
- if not function_name_list:
- return None
-
- evaluations = []
-
- for function_name in function_name_list:
- version_list = lambda_client.list_versions_by_function(FunctionName=function_name)
-
- if len(version_list['Versions']) <= 1:
- evaluations.append(build_evaluation(function_name, "NON_COMPLIANT", event, annotation="No version is present."))
- continue
-
- alias_list = list_all_lambda_aliases(lambda_client, function_name)
-
- if not alias_list:
- evaluations.append(build_evaluation(function_name, "NON_COMPLIANT", event, annotation="No alias is present."))
- continue
-
- is_alias_latest = False
- for alias in alias_list:
- if alias['FunctionVersion'] == '$LATEST':
- is_alias_latest = True
- break
-
- if is_alias_latest:
- evaluations.append(build_evaluation(function_name, "NON_COMPLIANT", event, annotation="Alias points to $LATEST version"))
- continue
-
- evaluations.append(build_evaluation(function_name, "COMPLIANT", event))
-
- return evaluations
-
-def list_all_lambda_function_names(client):
- function_list = client.list_functions(MaxItem=10000)
- name_list = []
- while True:
- for item in function_list['Functions']:
- name_list.append(item['FunctionName'])
- if 'NextMarker' in function_list:
- marker = function_list['NextMarker']
- function_list = client.list_functions(Marker=marker, MaxItem=10000)
- else:
- break
- return name_list
-
-def list_all_lambda_aliases(client, functionname):
- aliases = client.list_aliases(FunctionName=functionname)
- aliases_list = []
- while True:
- for alias_item in aliases['Aliases']:
- aliases_list.append(alias_item)
- if 'NextMarker' in aliases:
- marker = aliases['NextMarker']
- aliases = client.list_aliases(Marker=marker, FunctionName=functionname)
- else:
- break
- return aliases_list
-
-####################
-# Helper Functions #
-####################
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function to check if rule parameters exist
-def parameters_exist(parameters):
- return len(parameters) != 0
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evalations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evalations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evalations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
- AWS_CONFIG_CLIENT = get_client('config', event)
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- configuration_item = get_configuration_item(invoking_event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return {'internalErrorMessage': 'Unexpected message type ' + str(invoking_event)}
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- elif isinstance(compliance_result, str):
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while(evaluation_copy):
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=resultToken, TestMode=testMode)
- del evaluation_copy[:100]
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_CODE_IS_VERSIONED/LAMBDA_CODE_IS_VERSIONED_test.py b/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_CODE_IS_VERSIONED/LAMBDA_CODE_IS_VERSIONED_test.py
deleted file mode 100644
index fc63216..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_CODE_IS_VERSIONED/LAMBDA_CODE_IS_VERSIONED_test.py
+++ /dev/null
@@ -1,205 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-import sys
-import datetime
-
-config_client_mock = MagicMock()
-lambda_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- if client_name == 'lambda':
- return lambda_client_mock
- if client_name == 'sts':
- return sts_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('LAMBDA_CODE_IS_VERSIONED')
-
-class TestOnLambdaCodeIsVersioned(unittest.TestCase):
- functionListWithFunctions = {
- "Functions": [{
- "FunctionName": "lambda-code-is-versioned"
- }]}
- functionListWithoutFunctions = {"Functions": []}
-
-
- versionListWithVersioning = {
- "Versions": [
- {"Version": "$LATEST"},
- {"Version": "1"},
- {"Version": "2"},
- {"Version": "3"}
- ]
- }
- versionListWithoutVersioning = {
- "Versions": [{"Version": "$LATEST"}]}
-
- functionWithAliasNotPointingToLatest = {
- "Aliases": [
- {"FunctionVersion": "3"},
- {"FunctionVersion": "1"},
- {"FunctionVersion": "1"},
- {"FunctionVersion": "3"}
- ]
- }
- functionWithAliasAndPointingToLatest = {
- "Aliases": [
- {"FunctionVersion": "3"},
- {"FunctionVersion": "1"},
- {"FunctionVersion": "$LATEST"},
- {"FunctionVersion": "3"}
- ]
- }
- functionWithoutAlias = {"Aliases": []}
-
- complianceEvaluationResult = {
- "EvaluationResults": [
- {
- "EvaluationResultIdentifier": {
- "EvaluationResultQualifier": {
- "ResourceId": "rdkLambdaVersion",
- }
- },
- },
- ],
- }
- complianceEvaluationWithEmptyResult = {
- "EvaluationResults": []
- }
-
- def setUp(self):
- pass
-
- def test_no_lambda_function_present(self):
- lambda_client_mock.list_functions = MagicMock(return_value = self.functionListWithoutFunctions)
- config_client_mock.get_compliance_details_by_config_rule = MagicMock(return_value = self.complianceEvaluationWithEmptyResult)
- response = rule.lambda_handler(build_lambda_event(),{})
- resp_expected = []
- resp_expected.append({
- 'ComplianceResourceType' : 'AWS::::Account',
- 'ComplianceResourceId' : '123456789012',
- 'ComplianceType': "NOT_APPLICABLE"
- })
- assert_successful_evaluation(self,response, resp_expected)
-
- def test_no_versioning_for_lambda_function(self):
- lambda_client_mock.list_functions = MagicMock(return_value = self.functionListWithFunctions)
- lambda_client_mock.list_versions_by_function = MagicMock(return_value = self.versionListWithoutVersioning)
- config_client_mock.get_compliance_details_by_config_rule = MagicMock(return_value = self.complianceEvaluationWithEmptyResult)
- response = rule.lambda_handler(build_lambda_event(),{})
- resp_expected = []
- resp_expected.append({
- 'ComplianceResourceType' : 'AWS::Lambda::Function',
- 'ComplianceResourceId' : 'lambda-code-is-versioned',
- 'ComplianceType': "NON_COMPLIANT"
- })
- assert_successful_evaluation(self,response, resp_expected)
-
- def test_no_alias_present_for_lambda_function(self):
- lambda_client_mock.list_functions = MagicMock(return_value = self.functionListWithFunctions)
- lambda_client_mock.list_versions_by_function = MagicMock(return_value = self.versionListWithVersioning)
- lambda_client_mock.list_aliases = MagicMock(return_value = self.functionWithoutAlias)
- config_client_mock.get_compliance_details_by_config_rule = MagicMock(return_value = self.complianceEvaluationWithEmptyResult)
- response = rule.lambda_handler(build_lambda_event(),{})
- resp_expected = []
- resp_expected.append({
- 'ComplianceResourceType' : 'AWS::Lambda::Function',
- 'ComplianceResourceId' : 'lambda-code-is-versioned',
- 'ComplianceType': "NON_COMPLIANT"})
- assert_successful_evaluation(self,response, resp_expected)
-
- def test_alias_pointing_to_latest_version(self):
- lambda_client_mock.list_functions = MagicMock(return_value = self.functionListWithFunctions)
- lambda_client_mock.list_versions_by_function = MagicMock(return_value = self.versionListWithVersioning)
- lambda_client_mock.list_aliases = MagicMock(return_value = self.functionWithAliasAndPointingToLatest)
- config_client_mock.get_compliance_details_by_config_rule = MagicMock(return_value = self.complianceEvaluationWithEmptyResult)
- response = rule.lambda_handler(build_lambda_event(),{})
- resp_expected = []
- resp_expected.append({
- 'ComplianceResourceType' : 'AWS::Lambda::Function',
- 'ComplianceResourceId' : 'lambda-code-is-versioned',
- 'ComplianceType': "NON_COMPLIANT"
- })
- assert_successful_evaluation(self,response, resp_expected)
-
- def test_alias_pointing_not_pointing_to_latest(self):
- lambda_client_mock.list_functions = MagicMock(return_value = self.functionListWithFunctions)
- lambda_client_mock.list_versions_by_function = MagicMock(return_value = self.versionListWithVersioning)
- lambda_client_mock.list_aliases = MagicMock(return_value = self.functionWithAliasNotPointingToLatest)
- config_client_mock.get_compliance_details_by_config_rule = MagicMock(return_value = self.complianceEvaluationWithEmptyResult)
- response = rule.lambda_handler(build_lambda_event(),{})
- resp_expected = []
- resp_expected.append({
- 'ComplianceResourceType' : 'AWS::Lambda::Function',
- 'ComplianceResourceId' : 'lambda-code-is-versioned',
- 'ComplianceType': "COMPLIANT"
- })
- assert_successful_evaluation(self,response, resp_expected)
-
- def test_NOT_APPLICABLE_for_deleted_lambda_function(self):
- lambda_client_mock.list_functions = MagicMock(return_value = self.functionListWithFunctions)
- lambda_client_mock.list_versions_by_function = MagicMock(return_value = self.versionListWithVersioning)
- lambda_client_mock.list_aliases = MagicMock(return_value = self.functionWithAliasNotPointingToLatest)
- config_client_mock.get_compliance_details_by_config_rule = MagicMock(return_value = self.complianceEvaluationResult)
- response = rule.lambda_handler(build_lambda_event(),{})
- resp_expected = []
- resp_expected.append({
- 'ComplianceResourceType' : 'AWS::Lambda::Function',
- 'ComplianceResourceId' : 'rdkLambdaVersion',
- 'ComplianceType': "NOT_APPLICABLE"
- })
- resp_expected.append({
- 'ComplianceResourceType' : 'AWS::Lambda::Function',
- 'ComplianceResourceId' : 'lambda-code-is-versioned',
- 'ComplianceType': "COMPLIANT"
- })
- assert_successful_evaluation(self, response, resp_expected, 2)
-
-def build_lambda_event():
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- return {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': True,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
-}
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if "Annotation" in resp_expected:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for r in range(len(resp_expected)):
- testClass.assertEquals(resp_expected[r]['ComplianceType'], response[r]['ComplianceType'])
- testClass.assertEquals(resp_expected[r]['ComplianceResourceType'], response[r]['ComplianceResourceType'])
- testClass.assertEquals(resp_expected[r]['ComplianceResourceId'], response[r]['ComplianceResourceId'])
- testClass.assertTrue(response[r]['OrderingTimestamp'])
- if "Annotation" in resp_expected[r]:
- testClass.assertEquals(resp_expected[r]['Annotation'], response[r]['Annotation'])
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_CODE_IS_VERSIONED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_CODE_IS_VERSIONED/parameters.json
deleted file mode 100644
index a17786c..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_CODE_IS_VERSIONED/parameters.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "Parameters": {
- "RuleName": "LAMBDA_CODE_IS_VERSIONED",
- "SourceRuntime": "python3.6",
- "CodeKey": "LAMBDA_CODE_IS_VERSIONED.zip",
- "InputParameters": "{}",
- "SourceEvents": "AWS::Lambda::Function"
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED/parameters.json
deleted file mode 100644
index 891f8d4..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::Lambda::Function",
- "SourceIdentifier": "LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_ROLE_ALLOWED_ON_LOGGING/LAMBDA_ROLE_ALLOWED_ON_LOGGING.py b/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_ROLE_ALLOWED_ON_LOGGING/LAMBDA_ROLE_ALLOWED_ON_LOGGING.py
deleted file mode 100644
index 5186c11..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_ROLE_ALLOWED_ON_LOGGING/LAMBDA_ROLE_ALLOWED_ON_LOGGING.py
+++ /dev/null
@@ -1,352 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-'''
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- LAMBDA_ROLE_ALLOWED_ON_LOGGING
-
-Description:
- Ensure Lambda has the permission for logging. Each Lambda functions should have an IAM role with appropriate IAM permissions to publish its Lambda function logs to CloudWatch.
-
-Trigger:
- Configuration Change on AWS::Lambda::Function
-
-Reports on:
- AWS::Lambda::Function
-
-Rule Parameters:
- None
-
-Feature:
- In order to: ensure that role attached to any Lambda has permissions to create log events in CloudWatch log group
- As: a Security Officer
- I want: to ensure that any policy associated with role of Lambda has either 1) * (admin), 2) logs:* or 3) "logs:CreateLogGroup", "logs:CreateLogStream","logs:PutLogEvents" check these individual permissions.
-
-Scenarios:
-
- Scenario: 1
- Given: A Lambda is deleted
- Then: Return NOT_APPLICABLE
-
- Scenario: 2
- Given: A Lambda Role with the AWS-managed policy named "AWSLambdaBasicExecutionRole"
- Then: Return COMPLIANT
-
- Scenario: 3
- Given: A Lambda Role with no
- Then: Return NON_COMPLIANT
-
- Scenario: 4
- Given: At least one statement of Lambda Role has Action as "*"
- And: The resource-level condition is either * or arn:aws:logs:*
- Then: Return COMPLIANT
-
- Scenario: 5
- Given: At least one statement of Lambda Role has Action as "logs:*""
- And: The resource-level condition is either * or arn:aws:logs:*
- Then: Return COMPLIANT
-
- Scenario: 6
- Given: At least one statement of Lambda Role has Action as "logs:CreateLogGroup", "logs:CreateLogStream" and "logs:PutLogEvents"
- And: The resource-level condition is either * or arn:aws:logs:*
- Then: Return COMPLIANT
-
- Scenario: 7
- Given: Scenario 4/5/6 are not happening
- Then: Return NON_COMPLIANT
-
- Examples:
- | Policy |
- | inline user policy |
- | aws managed policy |
-
-Blind spot:
- 1) If the trust policy of the IAM Role is not allowing Lambda
- 2) A combinaison of policies gives the proper permissions.
- 3) A combinaison of inline and managed policies gives the proper permissions.
- 4) More than 100 policies are attached on role
- 5) Explicit Deny are not covered
- 6) NotAction are not covered
-'''
-
-import json
-import datetime
-import fnmatch
-import re
-import boto3
-import botocore.exceptions
-
-AWS_CONFIG_CLIENT = boto3.client('config')
-
-DEFAULT_RESOURCE_TYPE = "AWS::Lambda::Function"
-ASSUME_ROLE_MODE = True
-
-def evaluate_compliance(configuration_item, rule_parameters):
-
- role = configuration_item['relationships'][0]['resourceName']
- try:
- attachedpolicies = IAM_CLIENT.list_attached_role_policies(RoleName=role)
- if attachedpolicies['AttachedPolicies']:
- for policy in attachedpolicies['AttachedPolicies']:
- if policy['PolicyArn'] == "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole":
- return 'COMPLIANT'
- if is_a_role_managed_policy_allow_logging(attachedpolicies['AttachedPolicies']):
- return 'COMPLIANT'
-
- inlinepolicies = IAM_CLIENT.list_role_policies(RoleName=role)
- if inlinepolicies['PolicyNames']:
- if is_a_role_inline_policy_allow_logging(role, inlinepolicies['PolicyNames']):
- return 'COMPLIANT'
-
- except Exception as e:
- print("Exception:" + str(e) + "\nFunction: " + configuration_item['configuration']['functionName'])
- raise
-
- return 'NON_COMPLIANT'
-
-def is_a_role_inline_policy_allow_logging(roleName, inlinepolicies):
-
- for policy in inlinepolicies:
- getrolepolicy = IAM_CLIENT.get_role_policy(RoleName=roleName, PolicyName=policy)
- statements = getrolepolicy['PolicyDocument']['Statement']
- if are_statements_allow_logging(statements):
- return True
-
- return False
-
-def is_a_role_managed_policy_allow_logging(managedpolicies):
-
- for policy in managedpolicies:
- getrolepolicy = IAM_CLIENT.get_policy(PolicyArn=policy['PolicyArn'])
- getpolicyversion = IAM_CLIENT.get_policy_version(PolicyArn=policy['PolicyArn'], VersionId=getrolepolicy['Policy']['DefaultVersionId'])
- statements = getpolicyversion['PolicyVersion']['Document']['Statement']
-
- if are_statements_allow_logging(statements):
- return True
-
- return False
-
-def are_statements_allow_logging(statements):
-
- is_createloggroup_present = False
- is_createlogstream_present = False
- is_putlogevents_present = False
-
- for statement in statements:
- if not is_effect_allow(statement) or not is_resource_element_ok(statement):
- continue
-
- if isinstance(statement['Action'], list):
- for action in statement['Action']:
- if action == "*" or action == "log:*":
- return True
- elif action == "logs:CreateLogGroup":
- is_createloggroup_present = True
- elif action == "logs:CreateLogStream":
- is_createlogstream_present = True
- elif action == "logs:PutLogEvents":
- is_putlogevents_present = True
- else:
- if statement['Action'] == "*" or statement['Action'] == "log:*":
- return True
- elif statement['Action'] == "logs:CreateLogGroup":
- is_createloggroup_present = True
- elif statement['Action'] == "logs:CreateLogStream":
- is_createlogstream_present = True
- elif statement['Action'] == "logs:PutLogEvents":
- is_putlogevents_present = True
-
- return is_createloggroup_present and is_createlogstream_present and is_putlogevents_present
-
-def is_effect_allow(statement):
- return statement['Effect'] == "Allow"
-
-def is_resource_element_ok(statement):
- if isinstance(statement['Resource'], list):
- for resource in statement['Resource']:
- if "arn:aws:logs" in resource or "*" in resource:
- return True
- return False
- return fnmatch.fnmatch(statement['Resource'], 'arn:aws:logs:*') or statement['Resource'] == "*"
-
-# USE AS IS
-# Helper function to check if rule parameters exist
-def parameters_exist(parameters):
- return len(parameters) != 0
-
-# Helper function used to validate input
-def check_defined(reference, referenceName):
- if not reference:
- raise Exception('Error: ', referenceName, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(messageType):
- check_defined(messageType, 'messageType')
- return messageType == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(messageType):
- check_defined(messageType, 'messageType')
- return messageType == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resourceType, resourceId, configurationCaptureTime):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resourceType,
- resourceId=resourceId,
- laterTime=configurationCaptureTime,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event=None):
- if not event:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, timestamp, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- eval = {}
- if annotation:
- eval['Annotation'] = annotation
- eval['ComplianceResourceType'] = resource_type
- eval['ComplianceResourceId'] = resource_id
- eval['ComplianceType'] = compliance_type
- eval['OrderingTimestamp'] = timestamp
- return eval
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
- if ASSUME_ROLE_MODE:
- AWS_CONFIG_CLIENT = get_client('config', event)
-
- global IAM_CLIENT
- IAM_CLIENT = get_client('iam', event)
-
- evaluations = []
-
- #print(event)
- check_defined(event, 'event')
- invokingEvent = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- configuration_item = get_configuration_item(invokingEvent)
-
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(configuration_item, rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
-
- if isinstance(compliance_result, str):
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- evaluations.append(evaluation)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluations, ResultToken=resultToken, TestMode=testMode)
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_ROLE_ALLOWED_ON_LOGGING/LAMBDA_ROLE_ALLOWED_ON_LOGGING_test.py b/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_ROLE_ALLOWED_ON_LOGGING/LAMBDA_ROLE_ALLOWED_ON_LOGGING_test.py
deleted file mode 100644
index cd47793..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_ROLE_ALLOWED_ON_LOGGING/LAMBDA_ROLE_ALLOWED_ON_LOGGING_test.py
+++ /dev/null
@@ -1,441 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-import sys
-import json
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-
-CONFIG_CLIENT_MOCK = MagicMock()
-IAM_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- elif client_name == 'iam':
- return IAM_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('LAMBDA_ROLE_ALLOWED_ON_LOGGING')
-
-def assert_successful_evaluation(testClass, response, resp_expected):
- testClass.assertEquals(response[0]['ComplianceType'], resp_expected)
-
-def assert_customer_error_response(testClass, response, customerErrorCode=None, customerErrorMessage=None):
-
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
-
-def build_invoking_event(item_status='OK'):
- invoking_event = {
- "configurationItemDiff":"SomeDifference",
- "notificationCreationTime":"SomeTime",
- "messageType":"SomeType",
- "recordVersion":"SomeVersion",
- "configurationItem":
- {
- "relationships":[{"resourceName":"some_role"}],
- "configurationItemCaptureTime": "2018-05-11T17:53:48.872Z",
- "configurationItemStatus": item_status,
- "configurationStateId": "1526061228872",
- "arn": "arn:aws:lambda:us-east-1:823362693882:function:test-function",
- "resourceType": "AWS::Lambda::Function",
- "resourceId": "test-function",
- "resourceName": "test-function",
- "configuration": {
- "functionName": "test-function",
- "functionArn": "arn:aws:lambda:us-east-1:823362693882:function:test-function",
- "role": "some-role-arn"
- }
- }
- }
- return json.dumps(invoking_event)
-
-def build_lambda_event(ruleParameters='{}', invokingEvent=build_invoking_event()):
- return({
- "invokingEvent" : invokingEvent,
- "ruleParameters" : ruleParameters,
- "resultToken" : "TESTMODE",
- "eventLeftScope" : False,
- "executionRoleArn" : "arn:aws:iam::123456789012:role/service-role/config-role",
- "configRuleArn" : "arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan",
- "configRuleName" : "LAMBDA_ROLE_ALLOWED_ON_LOGGING",
- "configRuleId" : "config-rule-8fngan",
- "accountId" : "SampleID"
- })
-
-def gen_statement(action="*", resource="*", effect="Allow"):
- return {
- "Action": action,
- "Resource": resource,
- "Effect": effect
- }
-
-def gen_statement_list(statement=gen_statement(), statement2=False, statement3=False):
- statement_list = []
- statement_list.append(statement)
- if statement2:
- statement_list.append(statement2)
- if statement3:
- statement_list.append(statement3)
- return statement_list
-
-def gen_policy_api(type="inline", statement_list=gen_statement_list()):
- if type == "inline":
- return {
- "RoleName": "AdminLambda",
- "PolicyDocument": {
- "Version": "2012-10-17",
- "Statement": statement_list
- }
- }
- if type == "list_attached":
- return {
- "AttachedPolicies": [
- {
- "PolicyName": "some-policy-name",
- "PolicyArn": "some-policy-arn"
- }
- ]
- }
- if type == "get_policy":
- return {
- "Policy": {
- "PolicyName": "some-policy-name",
- "Arn": "some-policy-arn",
- "DefaultVersionId": "some-version-id"
- }
- }
- if type == "get_policy_version":
- return {
- "PolicyVersion": {
- "Document": {
- "Statement": statement_list
- }
- }
- }
-
-class TestScenario1DeletedRole(unittest.TestCase):
- def test_NOT_APPLICABLE_lambda_function_deleted(self):
- invokEvent = build_invoking_event("ResourceDeleted")
- lambdaEvent = build_lambda_event(invokingEvent=invokEvent)
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "NOT_APPLICABLE"
- assert_successful_evaluation(self, response, resp_expected)
-
-class TestScenario2AWSManagedRole(unittest.TestCase):
- def test_COMPLIANT_AWSLambdaBasicExecution_attached_on_role(self):
- list_attached_role_pl = {
- "AttachedPolicies": [
- {
- "PolicyName": "AWSLambdaBasicExecutionRole-11389b43-d62e-4847-a0af-a967a8e02578",
- "PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
- }
- ]
- }
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
-class TestScenario3NoPolicyOnRole(unittest.TestCase):
- def test_NON_COMPLIANT_no_policy_attached_on_role_case(self):
- list_attached_role_pl = {"AttachedPolicies": []}
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "NON_COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
-class TestScenario4ActionStar(unittest.TestCase):
-
- def test_COMPLIANT_action_star_allow_string_inline(self):
- get_pl = gen_policy_api()
- list_attached_role_pl = {"AttachedPolicies": []}
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": ["some-inline-name-policy"]})
- IAM_CLIENT_MOCK.get_role_policy = MagicMock(return_value=get_pl)
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_COMPLIANT_action_star_allow_list_inline(self):
- get_pl = gen_policy_api(statement_list=gen_statement_list(gen_statement(action=["some-action","*"])))
- list_attached_role_pl = {"AttachedPolicies": []}
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": ["some-inline-name-policy"]})
- IAM_CLIENT_MOCK.get_role_policy = MagicMock(return_value=get_pl)
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_NON_COMPLIANT_action_star_deny_inline(self):
- get_pl = gen_policy_api(statement_list=gen_statement_list(gen_statement(effect="Deny")))
- list_attached_role_pl = {"AttachedPolicies": []}
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": ["some-inline-name-policy"]})
- IAM_CLIENT_MOCK.get_role_policy = MagicMock(return_value=get_pl)
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "NON_COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_NON_COMPLIANT_action_star_other_resource_string_inline(self):
- get_pl = gen_policy_api(statement_list=gen_statement_list(gen_statement(resource="something_else")))
- list_attached_role_pl = {"AttachedPolicies": []}
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": ["some-inline-name-policy"]})
- IAM_CLIENT_MOCK.get_role_policy = MagicMock(return_value=get_pl)
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "NON_COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_COMPLIANT_action_star_resource_list_inline(self):
- get_pl = gen_policy_api(statement_list=gen_statement_list(gen_statement(resource=["something_else","*"])))
- list_attached_role_pl = {"AttachedPolicies": []}
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": ["some-inline-name-policy"]})
- IAM_CLIENT_MOCK.get_role_policy = MagicMock(return_value=get_pl)
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_COMPLIANT_action_star_allow_managed(self):
- list_attached_role_pl = gen_policy_api(type="list_attached")
- get_pl = gen_policy_api(type="get_policy")
- get_pl_version = gen_policy_api(type="get_policy_version")
-
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.get_policy = MagicMock(return_value=get_pl)
- IAM_CLIENT_MOCK.get_policy_version = MagicMock(return_value=get_pl_version)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": []})
-
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_NON_COMPLIANT_action_star_deny_managed(self):
- list_attached_role_pl = gen_policy_api(type="list_attached")
- get_pl = gen_policy_api(type="get_policy")
- get_pl_version = gen_policy_api(type="get_policy_version",statement_list=gen_statement_list(gen_statement(effect="Deny")))
-
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.get_policy = MagicMock(return_value=get_pl)
- IAM_CLIENT_MOCK.get_policy_version = MagicMock(return_value=get_pl_version)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": []})
-
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "NON_COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_COMPLIANT_action_star_resource_ok_managed(self):
- list_attached_role_pl = gen_policy_api(type="list_attached")
- get_pl = gen_policy_api(type="get_policy")
- get_pl_version = gen_policy_api(type="get_policy_version",statement_list=gen_statement_list(gen_statement(resource="arn:aws:logs:*")))
-
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.get_policy = MagicMock(return_value=get_pl)
- IAM_CLIENT_MOCK.get_policy_version = MagicMock(return_value=get_pl_version)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": []})
-
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
-class TestScenario5LogStar(unittest.TestCase):
-
- def test_COMPLIANT_action_logstar_allow_string_inline(self):
- get_pl = gen_policy_api(statement_list=gen_statement_list(gen_statement(action="log:*")))
- list_attached_role_pl = {"AttachedPolicies": []}
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": ["some-inline-name-policy"]})
- IAM_CLIENT_MOCK.get_role_policy = MagicMock(return_value=get_pl)
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_COMPLIANT_action_logstar_allow_list_inline(self):
- get_pl = gen_policy_api(statement_list=gen_statement_list(gen_statement(action=["some-action","log:*"])))
- list_attached_role_pl = {"AttachedPolicies": []}
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": ["some-inline-name-policy"]})
- IAM_CLIENT_MOCK.get_role_policy = MagicMock(return_value=get_pl)
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_NON_COMPLIANT_action_logstar_deny_inline(self):
- get_pl = gen_policy_api(statement_list=gen_statement_list(gen_statement(action="log:*",effect="Deny")))
- list_attached_role_pl = {"AttachedPolicies": []}
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": ["some-inline-name-policy"]})
- IAM_CLIENT_MOCK.get_role_policy = MagicMock(return_value=get_pl)
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "NON_COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_NON_COMPLIANT_action_logstar_other_resource_string_inline(self):
- get_pl = gen_policy_api(statement_list=gen_statement_list(gen_statement(action="log:*", resource="something_else")))
- list_attached_role_pl = {"AttachedPolicies": []}
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": ["some-inline-name-policy"]})
- IAM_CLIENT_MOCK.get_role_policy = MagicMock(return_value=get_pl)
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "NON_COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_COMPLIANT_action_logstar_resource_list_inline(self):
- get_pl = gen_policy_api(statement_list=gen_statement_list(gen_statement(action="log:*", resource=["something_else","*"])))
- list_attached_role_pl = {"AttachedPolicies": []}
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": ["some-inline-name-policy"]})
- IAM_CLIENT_MOCK.get_role_policy = MagicMock(return_value=get_pl)
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_COMPLIANT_action_logstar_allow_managed(self):
- list_attached_role_pl = gen_policy_api(type="list_attached")
- get_pl = gen_policy_api(type="get_policy")
- get_pl_version = gen_policy_api(type="get_policy_version",statement_list=gen_statement_list(gen_statement(action="log:*")))
-
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.get_policy = MagicMock(return_value=get_pl)
- IAM_CLIENT_MOCK.get_policy_version = MagicMock(return_value=get_pl_version)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": []})
-
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_NON_COMPLIANT_action_logstar_deny_managed(self):
- list_attached_role_pl = gen_policy_api(type="list_attached")
- get_pl = gen_policy_api(type="get_policy")
- get_pl_version = gen_policy_api(type="get_policy_version",statement_list=gen_statement_list(gen_statement(action="log:*",effect="Deny")))
-
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.get_policy = MagicMock(return_value=get_pl)
- IAM_CLIENT_MOCK.get_policy_version = MagicMock(return_value=get_pl_version)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": []})
-
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "NON_COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_COMPLIANT_action_logstar_resource_ok_managed(self):
- list_attached_role_pl = gen_policy_api(type="list_attached")
- get_pl = gen_policy_api(type="get_policy")
- get_pl_version = gen_policy_api(type="get_policy_version",statement_list=gen_statement_list(gen_statement(action="log:*",resource="arn:aws:logs:*")))
-
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.get_policy = MagicMock(return_value=get_pl)
- IAM_CLIENT_MOCK.get_policy_version = MagicMock(return_value=get_pl_version)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": []})
-
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
-class TestScenario6LogExactActions(unittest.TestCase):
- CreateLogGroup = gen_statement(action="logs:CreateLogGroup")
- CreateLogStream = gen_statement(action="logs:CreateLogStream")
- PutLogEvents = gen_statement(action="logs:PutLogEvents")
- PutLogEventsDeny = gen_statement(action="logs:PutLogEvents", effect="Deny")
- PutLogEventsBadResource = gen_statement(action="logs:PutLogEvents", resource="some-bad-resource")
- statement_list_all_in_one = gen_statement_list(gen_statement(action=["logs:CreateLogGroup","logs:CreateLogStream","logs:PutLogEvents"]))
- statement_list_all_in_three = gen_statement_list(CreateLogGroup, CreateLogStream, PutLogEvents)
- statement_list_all_in_three_with_deny = gen_statement_list(CreateLogGroup, CreateLogStream, PutLogEventsDeny)
- statement_list_all_in_three_with_bad_resource = gen_statement_list(CreateLogGroup, CreateLogStream, PutLogEventsBadResource)
-
- def test_COMPLIANT_action_logexactaction_inline(self):
- for state in [self.statement_list_all_in_one, self.statement_list_all_in_three]:
- get_pl = gen_policy_api(statement_list=state)
- list_attached_role_pl = {"AttachedPolicies": []}
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": ["some-inline-name-policy"]})
- IAM_CLIENT_MOCK.get_role_policy = MagicMock(return_value=get_pl)
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_NON_COMPLIANT_action_logexactaction_inline(self):
- for state in [self.statement_list_all_in_three_with_deny, self.statement_list_all_in_three_with_bad_resource]:
- get_pl = gen_policy_api(statement_list=state)
- list_attached_role_pl = {"AttachedPolicies": []}
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": ["some-inline-name-policy"]})
- IAM_CLIENT_MOCK.get_role_policy = MagicMock(return_value=get_pl)
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "NON_COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_COMPLIANT_action_logexactaction_managed(self):
- for state in [self.statement_list_all_in_one, self.statement_list_all_in_three]:
- list_attached_role_pl = gen_policy_api(type="list_attached")
- get_pl = gen_policy_api(type="get_policy")
- get_pl_version = gen_policy_api(type="get_policy_version",statement_list=state)
-
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.get_policy = MagicMock(return_value=get_pl)
- IAM_CLIENT_MOCK.get_policy_version = MagicMock(return_value=get_pl_version)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": []})
-
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_NOT_COMPLIANT_action_logexactaction_managed(self):
- for state in [self.statement_list_all_in_three_with_deny, self.statement_list_all_in_three_with_bad_resource]:
- list_attached_role_pl = gen_policy_api(type="list_attached")
- get_pl = gen_policy_api(type="get_policy")
- get_pl_version = gen_policy_api(type="get_policy_version",statement_list=state)
-
- IAM_CLIENT_MOCK.list_attached_role_policies = MagicMock(return_value=list_attached_role_pl)
- IAM_CLIENT_MOCK.get_policy = MagicMock(return_value=get_pl)
- IAM_CLIENT_MOCK.get_policy_version = MagicMock(return_value=get_pl_version)
- IAM_CLIENT_MOCK.list_role_policies = MagicMock(return_value={"PolicyNames": []})
-
- lambdaEvent = build_lambda_event()
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = "NON_COMPLIANT"
- assert_successful_evaluation(self, response, resp_expected)
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_ROLE_ALLOWED_ON_LOGGING/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_ROLE_ALLOWED_ON_LOGGING/parameters.json
deleted file mode 100644
index 7e55d84..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/LAMBDA_ROLE_ALLOWED_ON_LOGGING/parameters.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "Parameters": {
- "RuleName": "LAMBDA_ROLE_ALLOWED_ON_LOGGING",
- "SourceRuntime": "python3.6",
- "CodeKey": "LAMBDA_ROLE_ALLOWED_ON_LOGGING.zip",
- "InputParameters": "{}",
- "SourceEvents": "AWS::Lambda::Function"
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/MULTI_REGION_CLOUD_TRAIL_ENABLED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/MULTI_REGION_CLOUD_TRAIL_ENABLED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/MULTI_REGION_CLOUD_TRAIL_ENABLED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/MULTI_REGION_CLOUD_TRAIL_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/MULTI_REGION_CLOUD_TRAIL_ENABLED/parameters.json
deleted file mode 100644
index 97c0685..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/MULTI_REGION_CLOUD_TRAIL_ENABLED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "MULTI_REGION_CLOUD_TRAIL_ENABLED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{\"s3BucketName\": \"\", \"snsTopicArn\": \"\", \"cloudWatchLogsLogGroupArn\": \"\", \"includeManagementEvents\": \"\", \"readWriteType\": \"\"}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "MULTI_REGION_CLOUD_TRAIL_ENABLED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/RDS_ENHANCED_MONITORING_ENABLED/RDS_ENHANCED_MONITORING_ENABLED.py b/vendor/github.com/awslabs/aws-config-rules/python/RDS_ENHANCED_MONITORING_ENABLED/RDS_ENHANCED_MONITORING_ENABLED.py
deleted file mode 100644
index 58d61e8..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/RDS_ENHANCED_MONITORING_ENABLED/RDS_ENHANCED_MONITORING_ENABLED.py
+++ /dev/null
@@ -1,401 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-'''
-
-#####################################
-## Gherkin ##
-#####################################
-
-Description:
- Checks whether enhanced monitoring is enabled for Amazon RDS instances.
-Trigger:
- Configuration Changes
-Reports on:
- AWS::RDS::DBInstance
-Rule Parameters:
- monitoringInterval
- (Optional) An integer value in seconds between points when Enhanced Monitoring metrics are collected for the DB instance. Valid Values are 1, 5, 10, 15, 30, 60.
-
-Scenarios:
- Scenario: 1
- Given: Value for the rule parameter 'monitoringInterval' is invalid.
- Then: Return ERROR
- Scenario: 2
- Given: 'monitoringInterval' is '0' in configuration item of the Amazon RDS instance.
- Then: Return NON_COMPLIANT
- Scenario: 3
- Given: Value for the rule parameter 'monitoringInterval' is provided and is valid
- And: 'monitoringInterval' in configuration item of the Amazon RDS instance does not match the rule parameter value
- Then: Return NON_COMPLIANT
- Scenario: 4
- Given: Value for the rule parameter 'monitoringInterval' is not provided
- And: 'monitoringInterval' is a non-zero value in configuration item of the Amazon RDS instance.
- Then: Return COMPLIANT
- Scenario: 5
- Given: Value for the rule parameter 'monitoringInterval' is provided and is valid
- And: 'monitoringInterval' in configuration item of the Amazon RDS instance matches the rule parameter value
- Then: Return COMPLIANT
-
-'''
-
-import json
-import sys
-import datetime
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::RDS::DBInstance'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-ALLOWED_EM_VALUES = [1, 5, 10, 15, 30, 60]
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- print(configuration_item['configuration']['monitoringInterval'])
- print(valid_rule_parameters)
- #If 'monitoringInterval' is not set for the RDS Instance
- if int(configuration_item['configuration']['monitoringInterval']) == 0:
- return build_evaluation_from_config_item(configuration_item, 'NON_COMPLIANT', annotation="Enhanced Monitoring interval for this Amazon RDS instance is not configured.")
- #If rule parameter is not provided but the 'monitoringInterval' is set for the RDS Instance
- if not valid_rule_parameters and (int(configuration_item['configuration']['monitoringInterval']) > 0):
- return build_evaluation_from_config_item(configuration_item, 'COMPLIANT')
- #If rule parameter is set and the 'monitoringInterval' of the RDS instance matches with the rule parameter value
- if valid_rule_parameters and int(configuration_item['configuration']['monitoringInterval']) == int(valid_rule_parameters['monitoringInterval']):
- return build_evaluation_from_config_item(configuration_item, 'COMPLIANT')
- #If above conditions are not met that means rule parameter is valid but the 'monitoringInterval' value does not match with the rule parameter value
- return build_evaluation_from_config_item(configuration_item, 'NON_COMPLIANT', annotation="Enhanced Monitoring interval for this Amazon RDS instance is not set with period:" + valid_rule_parameters['monitoringInterval'])
-
-
-def evaluate_parameters(rule_parameters):
- if "monitoringInterval" not in rule_parameters:
- return {}
-
- if int(rule_parameters['monitoringInterval']) not in ALLOWED_EM_VALUES:
- raise ValueError('Invalid value for the parameter "monitoringInterval", Expected a valid integer from the list [1, 5, 10, 15, 30, 60].')
-
- return rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return status in ("OK", "ResourceDiscovered") and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/RDS_ENHANCED_MONITORING_ENABLED/RDS_ENHANCED_MONITORING_ENABLED_test.py b/vendor/github.com/awslabs/aws-config-rules/python/RDS_ENHANCED_MONITORING_ENABLED/RDS_ENHANCED_MONITORING_ENABLED_test.py
deleted file mode 100644
index c073db8..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/RDS_ENHANCED_MONITORING_ENABLED/RDS_ENHANCED_MONITORING_ENABLED_test.py
+++ /dev/null
@@ -1,195 +0,0 @@
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import json
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::RDS::DBInstance'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('RDS_ENHANCED_MONITORING_ENABLED')
-
-class ComplianceTest(unittest.TestCase):
- #scenario1
- rule_invalid_parameter = '{"monitoringInterval":"12"}'
- #scenario2,scenario5
- rule_valid_parameter = '{"monitoringInterval":"5"}'
- #scenario3
- rule_parameter_mismatch = '{"monitoringInterval":"10"}'
-
- valid_em_interval_configured = {
- "monitoringInterval": "5"
- }
-
- invalid_em_not_configured = {
- "monitoringInterval": "0"
- }
-
- def test_scenario_1_invalid_parameter_value(self):
- invoking_event = generate_invoking_event(self.valid_em_interval_configured)
- response = RULE.lambda_handler(
- build_lambda_configurationchange_event(invoking_event, rule_parameters=self.rule_invalid_parameter), {})
- assert_customer_error_response(self, response, 'Invalid value for the parameter "monitoringInterval", Expected a valid integer from the list [1, 5, 10, 15, 30, 60].')
-
- def test_scenario_2_interval_zero(self):
- invoking_event = generate_invoking_event(self.invalid_em_not_configured)
- response = RULE.lambda_handler(
- build_lambda_configurationchange_event(invoking_event, rule_parameters=self.rule_valid_parameter), {})
- assert_successful_evaluation(self, response, [build_expected_response('NON_COMPLIANT', 'test-instance', annotation="Enhanced Monitoring interval for this Amazon RDS instance is not configured.")])
-
- def test_scenario_3_interval_mismatch(self):
- invoking_event = generate_invoking_event(self.valid_em_interval_configured)
- response = RULE.lambda_handler(
- build_lambda_configurationchange_event(invoking_event, rule_parameters=self.rule_parameter_mismatch), {})
- assert_successful_evaluation(self, response, [build_expected_response('NON_COMPLIANT', 'test-instance', annotation="Enhanced Monitoring interval for this Amazon RDS instance is not set with period:10")])
-
- def test_scenario_4_empty_ruleparameter(self):
- invoking_event = generate_invoking_event(self.valid_em_interval_configured)
- response = RULE.lambda_handler(
- build_lambda_configurationchange_event(invoking_event, {}), {})
- assert_successful_evaluation(self, response, [build_expected_response('COMPLIANT', 'test-instance')])
-
- def test_scenario_5_em_interval_match(self):
- invoking_event = generate_invoking_event(self.valid_em_interval_configured)
- response = RULE.lambda_handler(
- build_lambda_configurationchange_event(invoking_event, rule_parameters=self.rule_valid_parameter), {})
- assert_successful_evaluation(self, response, [build_expected_response('COMPLIANT', 'test-instance')])
-
-####################
-# Helper Functions #
-####################
-
-def generate_invoking_event(test_configuration):
- invoking_event = '{"configurationItem":{"configuration":' \
- + json.dumps(test_configuration) \
- + ',"configurationItemCaptureTime":"2019-04-18T08:17:52.315Z","configurationItemStatus":"ResourceDiscovered","resourceType":"AWS::RDS::DBInstance","resourceId":"test-instance"},"messageType":"ConfigurationItemChangeNotification"}'
- return invoking_event
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': 'test-instance',
- 'configRuleArn': 'arn:aws:config:us-east-1:test-instance:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': 'test-instance',
- 'configRuleArn': 'arn:aws:config:us-east-1:test-instance:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/RDS_ENHANCED_MONITORING_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/RDS_ENHANCED_MONITORING_ENABLED/parameters.json
deleted file mode 100644
index a59db24..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/RDS_ENHANCED_MONITORING_ENABLED/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "CodeKey": "RDS_ENHANCED_MONITORING_ENABLED.zip",
- "SourceRuntime": "python3.6",
- "RuleName": "RDS_ENHANCED_MONITORING_ENABLED",
- "SourceEvents": "AWS::RDS::DBInstance",
- "OptionalParameters": "{}",
- "InputParameters": "{}"
- },
- "Tags": "[]"
-}
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/RDS_INSTANCE_PUBLIC_ACCESS_CHECK/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/RDS_INSTANCE_PUBLIC_ACCESS_CHECK/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/RDS_INSTANCE_PUBLIC_ACCESS_CHECK/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/RDS_INSTANCE_PUBLIC_ACCESS_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/RDS_INSTANCE_PUBLIC_ACCESS_CHECK/parameters.json
deleted file mode 100644
index a0bc0e0..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/RDS_INSTANCE_PUBLIC_ACCESS_CHECK/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "RDS_INSTANCE_PUBLIC_ACCESS_CHECK",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::RDS::DBInstance",
- "SourceIdentifier": "RDS_INSTANCE_PUBLIC_ACCESS_CHECK"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/RDS_MULTI_AZ_SUPPORT/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/RDS_MULTI_AZ_SUPPORT/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/RDS_MULTI_AZ_SUPPORT/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/RDS_MULTI_AZ_SUPPORT/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/RDS_MULTI_AZ_SUPPORT/parameters.json
deleted file mode 100644
index 5dad082..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/RDS_MULTI_AZ_SUPPORT/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "RDS_MULTI_AZ_SUPPORT",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::RDS::DBInstance",
- "SourceIdentifier": "RDS_MULTI_AZ_SUPPORT"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/RDS_SNAPSHOTS_PUBLIC_PROHIBITED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/RDS_SNAPSHOTS_PUBLIC_PROHIBITED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/RDS_SNAPSHOTS_PUBLIC_PROHIBITED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/RDS_SNAPSHOTS_PUBLIC_PROHIBITED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/RDS_SNAPSHOTS_PUBLIC_PROHIBITED/parameters.json
deleted file mode 100644
index 0746258..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/RDS_SNAPSHOTS_PUBLIC_PROHIBITED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "RDS_SNAPSHOTS_PUBLIC_PROHIBITED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::RDS::DBSnapshot",
- "SourceIdentifier": "RDS_SNAPSHOTS_PUBLIC_PROHIBITED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/RDS_STORAGE_ENCRYPTED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/RDS_STORAGE_ENCRYPTED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/RDS_STORAGE_ENCRYPTED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/RDS_STORAGE_ENCRYPTED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/RDS_STORAGE_ENCRYPTED/parameters.json
deleted file mode 100644
index 94b035b..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/RDS_STORAGE_ENCRYPTED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "RDS_STORAGE_ENCRYPTED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{\"kmsKeyId\": \"\"}",
- "SourceEvents": "AWS::RDS::DBInstance",
- "SourceIdentifier": "RDS_STORAGE_ENCRYPTED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/REDSHIFT_CLUSTER_CONFIGURATION_CHECK/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/REDSHIFT_CLUSTER_CONFIGURATION_CHECK/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/REDSHIFT_CLUSTER_CONFIGURATION_CHECK/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/REDSHIFT_CLUSTER_CONFIGURATION_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/REDSHIFT_CLUSTER_CONFIGURATION_CHECK/parameters.json
deleted file mode 100644
index 9fbbc93..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/REDSHIFT_CLUSTER_CONFIGURATION_CHECK/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "REDSHIFT_CLUSTER_CONFIGURATION_CHECK",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{\"clusterDbEncrypted\": \"true\", \"loggingEnabled\": \"true\"}",
- "OptionalParameters": "{\"nodeTypes\": \"dc1.large\"}",
- "SourceEvents": "AWS::Redshift::Cluster",
- "SourceIdentifier": "REDSHIFT_CLUSTER_CONFIGURATION_CHECK"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/REDSHIFT_CLUSTER_PUBLIC_ACCESS_CHECK/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/REDSHIFT_CLUSTER_PUBLIC_ACCESS_CHECK/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/REDSHIFT_CLUSTER_PUBLIC_ACCESS_CHECK/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/REDSHIFT_CLUSTER_PUBLIC_ACCESS_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/REDSHIFT_CLUSTER_PUBLIC_ACCESS_CHECK/parameters.json
deleted file mode 100644
index ebfac41..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/REDSHIFT_CLUSTER_PUBLIC_ACCESS_CHECK/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "REDSHIFT_CLUSTER_PUBLIC_ACCESS_CHECK",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::Redshift::Cluster",
- "SourceIdentifier": "REDSHIFT_CLUSTER_PUBLIC_ACCESS_CHECK"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ROOT_ACCOUNT_MFA_ENABLED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/ROOT_ACCOUNT_MFA_ENABLED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ROOT_ACCOUNT_MFA_ENABLED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ROOT_ACCOUNT_MFA_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/ROOT_ACCOUNT_MFA_ENABLED/parameters.json
deleted file mode 100644
index dea6afd..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ROOT_ACCOUNT_MFA_ENABLED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "ROOT_ACCOUNT_MFA_ENABLED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "ROOT_ACCOUNT_MFA_ENABLED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ROOT_NO_ACCESS_KEY/ROOT_NO_ACCESS_KEY.py b/vendor/github.com/awslabs/aws-config-rules/python/ROOT_NO_ACCESS_KEY/ROOT_NO_ACCESS_KEY.py
deleted file mode 100644
index 7f147d2..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ROOT_NO_ACCESS_KEY/ROOT_NO_ACCESS_KEY.py
+++ /dev/null
@@ -1,388 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-'''
-####################################
-# Gherkin ##
-####################################
-
-Rule Name:
- root-no-access-key
-
-Description:
- Ensure no root user access key exists
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::::Account
-
-Rule Parameters:
- None
-
-Feature:
- In order to: restrict privileged user
- As: a Security Officer
- I want: to ensure that no access key for the root user exists
-
-Scenarios:
- Scenario 1:
- Given: Access key for root user present
- And: Access key is active
- Then: return NON_COMPLIANT
-
- Scenario 2:
- Given: Access key for root user present
- And: Access key is inactive
- Then: return NON_COMPLIANT
-
- Scenario 3:
- Given: Access Key for root user is not present
- Then: COMPLIANT
-
-'''
-import json
-import datetime
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
- iam_client = get_client('iam', event)
- acc_summary = iam_client.get_account_summary()
-
- if acc_summary['SummaryMap']['AccountAccessKeysPresent'] == 0:
- return build_evaluation(event['accountId'], 'COMPLIANT', event)
-
- return build_evaluation(event['accountId'], 'NON_COMPLIANT', event, annotation='The root user has access key(s).')
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Parameter value is invalid",
- internalErrorDetails="An ValueError was raised during the validation of the Parameter value",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluations, ResultToken=resultToken, TestMode=testMode)
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ROOT_NO_ACCESS_KEY/ROOT_NO_ACCESS_KEY_test.py b/vendor/github.com/awslabs/aws-config-rules/python/ROOT_NO_ACCESS_KEY/ROOT_NO_ACCESS_KEY_test.py
deleted file mode 100644
index d5a9cfe..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ROOT_NO_ACCESS_KEY/ROOT_NO_ACCESS_KEY_test.py
+++ /dev/null
@@ -1,167 +0,0 @@
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-iam_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'sts':
- return sts_client_mock
- elif client_name == 'iam':
- return iam_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('ROOT_NO_ACCESS_KEY')
-
-class ComplianceTest(unittest.TestCase):
-
- def test_access_keys_present(self):
- iam_client_mock.reset_mock()
- summary = {'SummaryMap': { 'AccountAccessKeysPresent': 1}}
- iam_client_mock.get_account_summary = MagicMock(return_value=summary)
- response = rule.lambda_handler(build_lambda_scheduled_event(),{})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '123456789012', annotation='The root user has access key(s).'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_access_keys_not_present(self):
- iam_client_mock.reset_mock()
- summary = {'SummaryMap': { 'AccountAccessKeysPresent': 0}}
- iam_client_mock.get_account_summary = MagicMock(return_value=summary)
- response=rule.lambda_handler(build_lambda_scheduled_event(),{})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', '123456789012'))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(testClass, response, customerErrorCode=None, customerErrorMessage=None):
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- sts_client_mock.reset_mock(return_value=True)
- sts_client_mock.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ROOT_NO_ACCESS_KEY/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/ROOT_NO_ACCESS_KEY/parameters.json
deleted file mode 100644
index b715992..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ROOT_NO_ACCESS_KEY/parameters.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "ROOT_NO_ACCESS_KEY",
- "SourceRuntime": "python3.6",
- "CodeKey": "ROOT_NO_ACCESS_KEY.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourcePeriodic": "TwentyFour_Hours",
- "RuleSets": [
- "baseline",
- "rulecriticity:critical",
- "pci",
- "pci:7.1",
- "root"
- ]
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_LOGGING_ENABLED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_LOGGING_ENABLED/Readme.md
deleted file mode 100644
index 3311431..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_LOGGING_ENABLED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder assists you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_LOGGING_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_LOGGING_ENABLED/parameters.json
deleted file mode 100644
index 77902d7..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_LOGGING_ENABLED/parameters.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "S3_BUCKET_LOGGING_ENABLED",
- "SourceRuntime": null,
- "CodeKey": null,
- "SourceEvents": "AWS::S3::Bucket",
- "OptionalParameters": "{\"targetBucket\":\"\",\"targetPrefix\":\"\"}",
- "InputParameters": "{}",
- "SourceIdentifier": "S3_BUCKET_LOGGING_ENABLED",
- "RuleSets": [
- "baseline",
- "rulecriticity:high",
- "pci",
- "pci:10.1",
- "s3"
- ]
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_NAMING_CONVENTION/S3_BUCKET_NAMING_CONVENTION.py b/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_NAMING_CONVENTION/S3_BUCKET_NAMING_CONVENTION.py
deleted file mode 100644
index 6921cff..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_NAMING_CONVENTION/S3_BUCKET_NAMING_CONVENTION.py
+++ /dev/null
@@ -1,405 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-'''
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- S3_BUCKET_NAMING_CONVENTION
-
-Description:
- Verify that S3 Bucket name conforms to a particular regex pattern.
-
-Trigger:
- Configuration change
-
-Reports on:
- AWS::S3::Bucket
-
-Rule Parameters:
- regexPattern (Required)
- The string value provided to check with the S3 bucket name.
-
-Scenarios:
-
- Scenario 1:
- Given: Rule parameter regexPattern is not configured or not valid
- Then: Return ERROR
-
- Scenario 2:
- Given: Rule parameter regexPattern is configured and valid
- And: The Bucket name match the regexPattern
- Then: Return COMPLIANT
-
- Scenario 3:
- Given: Rule parameter regexPattern is configured and valid
- And: The Bucket name does not match the regexPattern
- Then: Return NON_COMPLIANT
-
-'''
-
-import json
-import sys
-import datetime
-import re
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::S3::Bucket'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
-
- s3_bucket_name = configuration_item["resourceName"]
- pattern_str = valid_rule_parameters["regexPattern"]
- pattern = re.compile(f"{pattern_str}")
-
- if pattern.match(s3_bucket_name):
- return build_evaluation_from_config_item(configuration_item, 'COMPLIANT')
-
- return build_evaluation_from_config_item(configuration_item, 'NON_COMPLIANT', 'The regex ({}) does not match ({}).'.format(pattern_str, s3_bucket_name))
-
-def evaluate_parameters(rule_parameters):
- try:
- re.compile(rule_parameters['regexPattern'])
- except:
- raise ValueError('This Config Rule must have a validate regex value specified for the parameter "regexPattern".')
-
- return rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event, region=None):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- region -- the region where the client is called (default: None)
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service, region)
- credentials = get_assume_role_credentials(event["executionRoleArn"], region)
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken'],
- region_name=region
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None). It will be truncated to 255 if longer.
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = build_annotation(annotation)
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None). It will be truncated to 255 if longer.
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = build_annotation(annotation)
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Build annotation within Service constraints
-def build_annotation(annotation_string):
- if len(annotation_string) > 256:
- return annotation_string[:244] + " [truncated]"
- return annotation_string
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
-
- return status in ('OK', 'ResourceDiscovered') and not event_left_scope
-
-
-def get_assume_role_credentials(role_arn, region=None):
- sts_client = boto3.client('sts', region)
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_NAMING_CONVENTION/S3_BUCKET_NAMING_CONVENTION_test.py b/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_NAMING_CONVENTION/S3_BUCKET_NAMING_CONVENTION_test.py
deleted file mode 100644
index e741587..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_NAMING_CONVENTION/S3_BUCKET_NAMING_CONVENTION_test.py
+++ /dev/null
@@ -1,160 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::S3::Bucket'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('S3_BUCKET_NAMING_CONVENTION')
-
-class ComplianceTest(unittest.TestCase):
-
- invoking_event_bucket = '{"configurationItem":{"configurationItemStatus":"ResourceDiscovered","configurationItemCaptureTime":"2018-07-02T03:37:52.418Z","resourceType":"AWS::S3::Bucket","resourceId":"apps-us-east-1","resourceName":"apps-us-east-1"},"notificationCreationTime":"2018-07-02T23:05:34.445Z","messageType":"ConfigurationItemChangeNotification"}'
- invoking_event_bucket_compliant = '{"configurationItem":{"configurationItemStatus":"ResourceDiscovered","configurationItemCaptureTime":"2018-07-02T03:37:52.418Z","resourceType":"AWS::S3::Bucket","resourceId":"apps-test-us-east-1","resourceName":"apps-test-us-east-1"},"notificationCreationTime":"2018-07-02T23:05:34.445Z","messageType":"ConfigurationItemChangeNotification"}'
-
- def setUp(self):
- pass
-
- def test_scenario_1_invalid(self):
- invoking_event = self.invoking_event_bucket
- lambda_event = build_lambda_configurationchange_event(invoking_event, rule_parameters="{\"regexPattern\":\"[bad\"}")
- response = RULE.lambda_handler(lambda_event, {})
- assert_customer_error_response(self, response, "InvalidParameterValueException")
-
- def test_scenario_2_non_compliant(self):
- invoking_event = self.invoking_event_bucket
- lambda_event = build_lambda_configurationchange_event(invoking_event, rule_parameters="{\"regexPattern\":\".*test.*\"}")
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', "apps-us-east-1", annotation='The regex (.*test.*) does not match (apps-us-east-1).'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario_3_compliant(self):
- invoking_event = self.invoking_event_bucket_compliant
- lambda_event = build_lambda_configurationchange_event(invoking_event, rule_parameters="{\"regexPattern\":\".*test.*\"}")
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', "apps-test-us-east-1"))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_NAMING_CONVENTION/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_NAMING_CONVENTION/parameters.json
deleted file mode 100644
index 03b46ab..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_NAMING_CONVENTION/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "S3_BUCKET_NAMING_CONVENTION",
- "SourceRuntime": "python3.6",
- "CodeKey": "S3_BUCKET_NAMING_CONVENTION.zip",
- "InputParameters": "{\"regexPattern\":\".*test.*\"}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::S3::Bucket"
- },
- "Tags": "[]"
-}
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_PUBLIC_READ_PROHIBITED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_PUBLIC_READ_PROHIBITED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_PUBLIC_READ_PROHIBITED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_PUBLIC_READ_PROHIBITED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_PUBLIC_READ_PROHIBITED/parameters.json
deleted file mode 100644
index 0f47420..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_PUBLIC_READ_PROHIBITED/parameters.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "S3_BUCKET_PUBLIC_READ_PROHIBITED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::S3::Bucket",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "S3_BUCKET_PUBLIC_READ_PROHIBITED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_PUBLIC_WRITE_PROHIBITED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_PUBLIC_WRITE_PROHIBITED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_PUBLIC_WRITE_PROHIBITED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_PUBLIC_WRITE_PROHIBITED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_PUBLIC_WRITE_PROHIBITED/parameters.json
deleted file mode 100644
index 2b94cdf..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_PUBLIC_WRITE_PROHIBITED/parameters.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "S3_BUCKET_PUBLIC_WRITE_PROHIBITED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::S3::Bucket",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "S3_BUCKET_PUBLIC_WRITE_PROHIBITED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_SSL_REQUESTS_ONLY/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_SSL_REQUESTS_ONLY/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_SSL_REQUESTS_ONLY/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_SSL_REQUESTS_ONLY/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_SSL_REQUESTS_ONLY/parameters.json
deleted file mode 100644
index 75ed017..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_SSL_REQUESTS_ONLY/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "S3_BUCKET_SSL_REQUESTS_ONLY",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::S3::Bucket",
- "SourceIdentifier": "S3_BUCKET_SSL_REQUESTS_ONLY"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_VERSIONING_ENABLED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_VERSIONING_ENABLED/Readme.md
deleted file mode 100644
index 3311431..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_VERSIONING_ENABLED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder assists you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_VERSIONING_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_VERSIONING_ENABLED/parameters.json
deleted file mode 100644
index 56fd10e..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_BUCKET_VERSIONING_ENABLED/parameters.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "S3_BUCKET_VERSIONING_ENABLED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{\"isMfaDeleteEnabled\":\"\"}",
- "SourceEvents": "AWS::S3::Bucket",
- "SourceIdentifier": "S3_BUCKET_VERSIONING_ENABLED",
- "RuleSets": [
- "rulecriticity:high",
- "pci",
- "pci:12.10",
- "s3"
- ]
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT/README.md b/vendor/github.com/awslabs/aws-config-rules/python/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT/README.md
deleted file mode 100644
index 41cf29b..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT/README.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Warning!!! This rule requires an additional step to use.
-Prior to rule deployment additional libraries must be added to make this rule function as it requires versions of BOTO3 and BOTOCORE greater than what can be currently supported by AWS lambda.
-
-1. Change directory to the parent folder of S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT
-2. Create newboto directory
-
- ``` mkdir S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT/newboto ```
-3. Add current libraries to S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT/newboto folder
-
-```pip3 install boto3 botocore urllib3 --system --no-deps --target='S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT/newboto/'```
-
-4. Deploy as normal
-
- ``` rdk deploy S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT ```
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT.PY b/vendor/github.com/awslabs/aws-config-rules/python/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT.PY
deleted file mode 100644
index 58315bd..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT.PY
+++ /dev/null
@@ -1,444 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-'''
-#####################################
-## Gherkin ##
-#####################################
-Rule Name:
- S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT
-Description:
- Checks whether AWS S3 public access account settings match the assigned parameters.
-Trigger:
- Periodic
-Resource Type to report on:
- AWS::::Account
-Rule Parameters:
- | ---------------------- | ---------- | -----------------------------------------------------------------------------------------|
- | Parameter Name | Type | Description |
- | ---------------------- | ---------- | -----------------------------------------------------------------------------------------|
- | BlockPublicAcls | Optional | Block new public ACLs and uploading public objects (Default True) |
- | IgnorePublicAcls | Optional | Remove public access granted through public ACLs (Default True) |
- | BlockPublicPolicy | Optional | Block new public bucket policies (Default True) |
- | RestrictPublicBuckets | Optional | Block public and cross-account access to buckets that have public policies (Default True)|
- | ---------------------- | ---------- | -----------------------------------------------------------------------------------------|
-Feature:
- In order to: ensure that s3 account settings are being restricted to the appropriate level
- As: a Security Officer
- I want: to verify that the configuration of s3 account public access settings is correct.
-Scenarios:
- Scenario 1:
- Given: Given: Input paramters are configured
- And: At least one Input Paramter is invalid
- then: Return an error
- Scenario 2:
- Given: Given: All input parameters are valid
- And: At least 1 S3 config parameter does not match the corresponding value for the account
- then: Return NON_COMPLIANT
- Scenario 3:
- Given: Given: No input parameters are defined
- And: At least 1 default value does not match the corresponding value for the account
- then: Return NON_COMPLIANT
- Scenario 4:
- Given: Given: No input parameters are defined
- And: All S3 public access account settings match the default values
- then: Return COMPLIANT
- Scenario 5:
- Given: Given: All input parameters are valid
- And: All S3 config parameters match the corresponding value for the account
- then: Return COMPLIANT
-'''
-
-import json
-import datetime
-import os
-import os.path
-import sys
-ENVLAMBDATASKROOT = os.environ["LAMBDA_TASK_ROOT"]
-sys.path.insert(0, ENVLAMBDATASKROOT+"/boto3-1-9-82") #hack to change the version of boto
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
-
- client = get_client('s3control', event)
- response = client.get_public_access_block(AccountId=event['accountId'])
- evaluations = []
-
- if (response['PublicAccessBlockConfiguration']['BlockPublicAcls'] == valid_rule_parameters['BlockPublicAcls']) \
- and (response['PublicAccessBlockConfiguration']['IgnorePublicAcls'] == valid_rule_parameters['IgnorePublicAcls']) \
- and (response['PublicAccessBlockConfiguration']['BlockPublicPolicy'] == valid_rule_parameters['BlockPublicPolicy']) \
- and (response['PublicAccessBlockConfiguration']['RestrictPublicBuckets'] == valid_rule_parameters['RestrictPublicBuckets']):
- evaluations.append(build_evaluation(event['accountId'], 'COMPLIANT', event))
-
- else:
- annotationbuilder = 'BlockPublicAcls:' + str(response['PublicAccessBlockConfiguration']['BlockPublicAcls']) + ' ' \
- 'IgnorePublicAcls:' + str(response['PublicAccessBlockConfiguration']['IgnorePublicAcls']) + ' ' \
- 'BlockPublicPolicy:' + str(response['PublicAccessBlockConfiguration']['BlockPublicPolicy']) + ' ' \
- 'RestrictPublicBuckets:' + str(response['PublicAccessBlockConfiguration']['RestrictPublicBuckets'])
- evaluations.append(build_evaluation(event['accountId'], 'NON_COMPLIANT', event, annotation=annotationbuilder))
- return evaluations
-
-def evaluate_parameters(rule_parameters):
-
- valid_rule_parameters = {}
-
- if 'BlockPublicAcls' not in rule_parameters:
- rule_parameters['BlockPublicAcls'] = True
- if 'IgnorePublicAcls' not in rule_parameters:
- rule_parameters['IgnorePublicAcls'] = True
- if 'BlockPublicPolicy' not in rule_parameters:
- rule_parameters['BlockPublicPolicy'] = True
- if 'RestrictPublicBuckets' not in rule_parameters:
- rule_parameters['RestrictPublicBuckets'] = True
- valid_rule_parameters['BlockPublicAcls'] = to_bool(rule_parameters['BlockPublicAcls'])
- valid_rule_parameters['IgnorePublicAcls'] = to_bool(rule_parameters['IgnorePublicAcls'])
- valid_rule_parameters['BlockPublicPolicy'] = to_bool(rule_parameters['BlockPublicPolicy'])
- valid_rule_parameters['RestrictPublicBuckets'] = to_bool(rule_parameters['RestrictPublicBuckets'])
-
- return valid_rule_parameters
-
-def to_bool(value):
- """
- Convert input string into a boolean. Throw exception if it gets a string it doesn't handle.
- Case is ignored for strings. These string values are handled:
- True: 'True', "1", "TRue", "yes", "y", "t"
- False: "", "0", "faLse", "no", "n", "f"
- Non-string values are passed to bool.
- """
- if isinstance(value, str):
- if value.lower() in ("yes", "y", "true", "t", "1"):
- return True
- if value.lower() in ("no", "n", "false", "f", "0", ""):
- return False
- raise Exception('Invalid value for boolean conversion: ' + value)
- return bool(value)
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT_TEST.py b/vendor/github.com/awslabs/aws-config-rules/python/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT_TEST.py
deleted file mode 100644
index 07562d7..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT_TEST.py
+++ /dev/null
@@ -1,197 +0,0 @@
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-S3_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 's3control':
- return S3_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT')
-
-class ComplianceTestScenarios(unittest.TestCase):
-
- s3_account_settings = {
- 'BlockPublicAcls': True,
- 'IgnorePublicAcls': True,
- 'BlockPublicPolicy': True,
- 'RestrictPublicBuckets': True
- }
-
- scenario1_invalid_inputs = '{"BlockPublicAcls": "Truee", "IgnorePublicAcls": "True", "BlockPublicPolicy": "True", "RestrictPublicBuckets": "True"}'
- scenario2_mismatched_inputs = '{"BlockPublicAcls": "False", "IgnorePublicAcls": "True", "BlockPublicPolicy": "True", "RestrictPublicBuckets": "True"}'
- scenario3_empty_inputs_nonmatch = {}
- scenario4_empty_inputs_match = {}
- scenario5_valid_inputs = '{"BlockPublicAcls": "True", "IgnorePublicAcls": "True", "BlockPublicPolicy": "True", "RestrictPublicBuckets": "True"}'
-
- def test_scenario1_invalid_parameters(self):
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_scheduled_event(self.scenario1_invalid_inputs), {})
- assert_customer_error_response(self, response, "InvalidParameterValueException")
-
- def test_scenario2_not_matching_parameters(self):
- RULE.ASSUME_ROLE_MODE = False
- S3_CLIENT_MOCK.PublicAccessBlockConfiguration = MagicMock(return_value=self.s3_account_settings)
- response = RULE.lambda_handler(build_lambda_scheduled_event(self.scenario2_mismatched_inputs), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '123456789012', 'AWS::::Account', 'BlockPublicAcls:True IgnorePublicAcls:True BlockPublicPolicy:True RestrictPublicBuckets:True'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario3_not_matching_empty_parameters(self):
- RULE.ASSUME_ROLE_MODE = False
- S3_CLIENT_MOCK.PublicAccessBlockConfiguration = MagicMock(return_value=self.s3_account_settings)
- response = RULE.lambda_handler(build_lambda_scheduled_event(self.scenario3_empty_inputs_nonmatch), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', '123456789012', 'AWS::::Account', 'BlockPublicAcls:True IgnorePublicAcls:True BlockPublicPolicy:True RestrictPublicBuckets:True'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario4_empty_inputs_that_match(self):
- RULE.ASSUME_ROLE_MODE = False
- S3_CLIENT_MOCK.PublicAccessBlockConfiguration = MagicMock(return_value=self.s3_account_settings)
- response = RULE.lambda_handler(build_lambda_scheduled_event(self.scenario4_empty_inputs_match), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_scenario5_valid_parameters(self):
- RULE.ASSUME_ROLE_MODE = False
- S3_CLIENT_MOCK.PublicAccessBlockConfiguration = MagicMock(return_value=self.s3_account_settings)
- response = RULE.lambda_handler(build_lambda_scheduled_event(self.scenario5_valid_inputs), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT/parameters.json
deleted file mode 100644
index 12ac5b9..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT",
- "SourceRuntime": "python3.6",
- "CodeKey": "S3_PUBLIC_ACCESS_SETTINGS_FOR_ACCOUNT.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{\"BlockPublicAcls\": \"\", \"IgnorePublicAcls\": \"\", \"BlockPublicPolicy\": \"\", \"RestrictPublicBuckets\": \"\"}",
- "SourcePeriodic": "One_Hour"
- },
- "Tags": "[]"
-}
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_VPC_ENDPOINT_ENABLED/S3_VPC_ENDPOINT_ENABLED.py b/vendor/github.com/awslabs/aws-config-rules/python/S3_VPC_ENDPOINT_ENABLED/S3_VPC_ENDPOINT_ENABLED.py
deleted file mode 100644
index 3fb5f6e..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_VPC_ENDPOINT_ENABLED/S3_VPC_ENDPOINT_ENABLED.py
+++ /dev/null
@@ -1,391 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-'''
-Description
- Check whether VPC Endpoint is enabled for each VPC to access Amazon S3.
-
-Trigger
- Periodic (because VPC Configuration Item does not record Endpoint status)
-
-Reports on:
- AWS::EC2::VPC
-
-Rule Parameters:
- None
-
-Scenarios:
- Scenario 1:
- Given: No VPC is present while using the describe_vpcs() API call
- Then: Return NOT_APPLICABLE
-
- Scenario 2:
- Given: At least one VPC is present
- And: The S3 service is not present in the list of "ServiceName" on DescribeVpcEndpoints API
- Then: Return NON_COMPLIANT on this VPC
-
- Scenario 3:
- Given: At least one VPC is present
- And: The S3 service is present in the "ServiceName" key on DescribeVpcEndpoints API
- And: The "State" key value is not "Available"
- Then: Return NON_COMPLIANT on this VPC
-
- Scenario 4:
- Given: At least one VPC is present
- And: The S3 service is present in the "ServiceName" key on DescribeVpcEndpoints API
- And: The "State" key value is "Available"
- Then: Return COMPLIANT on this VPC
-'''
-import json
-import sys
-import datetime
-import boto3
-import botocore
-
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::EC2::VPC'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def get_vpcendpoints(vpc_id, event):
- compliance_results = {}
- ec2_client = get_client('ec2', event)
- compliance = 'NON_COMPLIANT'
- region = get_region_from_config_arn(event)
- annotate = 'There are no Amazon S3 VPC endpoints present in '+ vpc_id+'.'
- response = ec2_client.describe_vpc_endpoints(Filters=[
- {
- 'Name':'vpc-id',
- 'Values':[vpc_id]
- },
- {
- 'Name':'service-name',
- 'Values': ['com.amazonaws.'+region+'.s3']
- }])
- if response['VpcEndpoints'] != []:
- for vpce in response['VpcEndpoints']:
- endpoint_state = vpce['State']
- annotate = 'The Amazon S3 VPC endpoint is not in Available state '+vpc_id+'.'
- if is_available(endpoint_state):
- annotate = None
- compliance = 'COMPLIANT'
- compliance_results['%s' %(vpc_id)] = [annotate, compliance]
- return compliance_results
-
-def get_region_from_config_arn(event):
- return event['configRuleArn'].split(':')[3]
-
-def is_available(endpointstate):
- return endpointstate == 'available'
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- evaluations = []
- ec2_client = get_client('ec2', event)
- vpc_response = ec2_client.describe_vpcs()
- if not vpc_response['Vpcs']:
- evaluations.append(build_evaluation(event['accountId'], 'NOT_APPLICABLE', event))
- return evaluations
- for vpc in vpc_response['Vpcs']:
- evaluation_payload = get_vpcendpoints(vpc['VpcId'], event)
- evaluations.append(build_evaluation(vpc['VpcId'], evaluation_payload[vpc['VpcId']][1], event, annotation=evaluation_payload[vpc['VpcId']][0]))
- return evaluations
-
-def evaluate_parameters(rule_parameters):
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return status in ('OK', 'ResourceDiscovered') and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_VPC_ENDPOINT_ENABLED/S3_VPC_ENDPOINT_ENABLED_test.py b/vendor/github.com/awslabs/aws-config-rules/python/S3_VPC_ENDPOINT_ENABLED/S3_VPC_ENDPOINT_ENABLED_test.py
deleted file mode 100644
index ef4dca8..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_VPC_ENDPOINT_ENABLED/S3_VPC_ENDPOINT_ENABLED_test.py
+++ /dev/null
@@ -1,245 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import botocore
-
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::EC2::VPC'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-EC2_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 'ec2':
- return EC2_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('S3_VPC_ENDPOINT_ENABLED')
-
-class ComplianceTest(unittest.TestCase):
- # Unit test for no VPC is present -- GHERKIN Scenario 1
- def test_scenario_1(self):
- EC2_CLIENT_MOCK.reset_mock()
- describevpc_return = {"Vpcs":[]}
- EC2_CLIENT_MOCK.describe_vpcs = MagicMock(return_value=describevpc_return)
- resp_expected = []
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected.append(build_expected_response('NOT_APPLICABLE', '123456789012', compliance_resource_type='AWS::EC2::VPC'))
- assert_successful_evaluation(self, response, resp_expected)
-
- #Unit test for VPC is present and no AWS S3 VPC endpoints are present for that VPC -- GHERKIN Scenario 2
- def test_scenario_2(self):
- EC2_CLIENT_MOCK.reset_mock()
- vpc_id = "vpc-1234567"
- describevpc_return = {"Vpcs":[{"VpcId":vpc_id}]}
- EC2_CLIENT_MOCK.describe_vpcs = MagicMock(return_value=describevpc_return)
- describevpcendpoints_return = {"VpcEndpoints":[]}
- EC2_CLIENT_MOCK.describe_vpc_endpoints = MagicMock(return_value=describevpcendpoints_return)
- resp_expected = []
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'vpc-1234567', compliance_resource_type='AWS::EC2::VPC', annotation='There are no Amazon S3 VPC endpoints present in '+ vpc_id+'.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- #Unit test for when Amazon VPC has a AWS S3 VPC present but is not in available state -- Gherkin Scenario 3
- def test_scenario_3(self):
- EC2_CLIENT_MOCK.reset_mock()
- resp_expected = []
- vpc_id = "vpc-1234567"
- vpc_return = {'Vpcs':[{'VpcId':'vpc-1234567'}]}
- EC2_CLIENT_MOCK.describe_vpcs = MagicMock(return_value=vpc_return)
- describevpcendpoints_return = {
- "VpcEndpoints": [
- {
- "VpcEndpointId": "vpce-06e48c97f0bc5d501",
- "VpcEndpointType": "Gateway",
- "VpcId": "vpc-0056487b",
- "ServiceName": "com.amazonaws.us-east-1.s3",
- "State": "pending",
- "PolicyDocument": "{\"Version\":\"2008-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"*\",\"Resource\":\"*\"}]}",
- "RouteTableIds": [
- "rtb-0ba1ba4f40636b1fd"
- ]
- }
- ]
- }
- EC2_CLIENT_MOCK.describe_vpc_endpoints = MagicMock(return_value=describevpcendpoints_return)
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'vpc-1234567', compliance_resource_type='AWS::EC2::VPC', annotation='The Amazon S3 VPC endpoint is not in Available state '+vpc_id+'.'))
- assert_successful_evaluation(self, response, resp_expected)
-
- #Unit test for VPC is present, and S3 endpoint is present in available state -- Gherkin Scenario 4
- def test_scenario_4(self):
- EC2_CLIENT_MOCK.reset_mock()
- resp_expected = []
- vpc_return = {'Vpcs':[{'VpcId':'vpc-1234567'}]}
- EC2_CLIENT_MOCK.describe_vpcs = MagicMock(return_value=vpc_return)
- describevpcendpoints_return = {
- "VpcEndpoints": [
- {
- "VpcEndpointId": "vpce-06e48c97f0bc5d501",
- "VpcEndpointType": "Gateway",
- "VpcId": "vpc-0056487b",
- "ServiceName": "com.amazonaws.us-east-1.s3",
- "State": "available",
- "PolicyDocument": "{\"Version\":\"2008-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"*\",\"Resource\":\"*\"}]}",
- "RouteTableIds": [
- "rtb-0ba1ba4f40636b1fd"
- ]
- },
- {
- "VpcEndpointId": "vpce-03ff48d1f2709aa88",
- "VpcEndpointType": "Interface",
- "VpcId": "vpc-0056487b",
- "ServiceName": "com.amazonaws.us-east-1.secretsmanager",
- "State": "available",
- "PolicyDocument": "{\n \"Statement\": [\n {\n \"Action\": \"*\", \n \"Effect\": \"Allow\", \n \"Principal\": \"*\", \n \"Resource\": \"*\"\n }\n ]\n}",
- "RouteTableIds": [],
- "SubnetIds": [
- "subnet-711b7d3b"
- ]
- }
- ]
- }
- EC2_CLIENT_MOCK.describe_vpc_endpoints = MagicMock(return_value=describevpcendpoints_return)
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected.append(build_expected_response('COMPLIANT', 'vpc-1234567', compliance_resource_type='AWS::EC2::VPC'))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/S3_VPC_ENDPOINT_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/S3_VPC_ENDPOINT_ENABLED/parameters.json
deleted file mode 100644
index 176b827..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/S3_VPC_ENDPOINT_ENABLED/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "CodeKey": "S3_VPC_ENDPOINT_ENABLED.zip",
- "SourceRuntime": "python3.6",
- "SourcePeriodic": "TwentyFour_Hours",
- "RuleName": "S3_VPC_ENDPOINT_ENABLED",
- "OptionalParameters": "{}",
- "InputParameters": "{}"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED/SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED.py b/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED/SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED.py
deleted file mode 100644
index d6690b0..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED/SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED.py
+++ /dev/null
@@ -1,451 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-"""
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED
-
-Description:
- Check whether an AWS KMS key is configured for an Amazon SageMaker Endpoint Config.
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::SageMaker::EndpointConfig
-
-Rule Parameters:
- keyArns(Optional) Comma-separated list of allowed AWS KMS key IDs.
-
-Scenarios:
- Scenario: 1
- Given: The rule parameter 'keyArns' is provided and is invalid
- Then: Return ERROR
- Scenario: 2
- Given: No Amazon SageMaker endpoint configs exist
- Then: Return NOT_APPLICABLE
- Scenario: 3
- Given: At least one Amazon SageMaker endpoint config exists
- And: 'KmsKeyId' is not specified for the Amazon SageMaker Endpoint Config
- Then: Return NON_COMPLIANT with annotation "No AWS KMS Key is configured for this Amazon SageMaker Endpoint Config."
- Scenario: 4
- Given: The rule parameter 'keyArns' is provided and is valid
- And: At least one Amazon SageMaker endpoint config exists
- And: 'KmsKeyId' is specified for the Amazon SageMaker Endpoint Config
- And: None of the AWS KMS key IDs specified in the rule parameter 'keyArns' match 'KmsKeyId'
- Then: Return NON_COMPLIANT with annotation "AWS KMS Key configured for this Amazon SageMaker Endpoint Config is not an KMS Key allowed in the rule parameter (keyArns): ."
- Scenario: 5
- Given: The rule parameter 'keyArns' is provided and is valid
- And: At least one Amazon SageMaker endpoint config exists
- And: 'KmsKeyId' is specified in the Amazon SageMaker Endpoint Config
- And: One of the AWS KMS key IDs specified in the rule parameter 'keyArns' matches 'KmsKeyId'
- Then: Return COMPLIANT
- Scenario: 6
- Given: The rule parameter 'keyArns' is not provided
- And: At least one Amazon SageMaker endpoint config exists
- And: 'KmsKeyId' is specified in the Amazon SageMaker Endpoint Config
- Then: Return COMPLIANT
-"""
-
-import json
-import sys
-import datetime
-import re
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def get_all_endpoint_config_names(client):
- #This function returns all the SageMaker Endpoint Configs
-
- list_of_endpoint_config = []
- paginator = client.get_paginator('list_endpoint_configs')
- page_iterator = paginator.paginate()
- for page in page_iterator:
- for endpoint_config in page['EndpointConfigs']:
- list_of_endpoint_config.append(endpoint_config['EndpointConfigName'])
- return list_of_endpoint_config
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- evaluations = []
- sagemaker_client = get_client('sagemaker', event)
- endpoint_config_list = get_all_endpoint_config_names(sagemaker_client)
-
- #SCENARIO 2: No Amazon SageMaker endpoint configs exist
- if not endpoint_config_list:
- return None
-
- for endpoint_config in endpoint_config_list:
- sagemaker_endpoint_details = sagemaker_client.describe_endpoint_config(EndpointConfigName=endpoint_config)
-
- if 'KmsKeyId' in sagemaker_endpoint_details:
- #SCENARIO 6: The rule parameter 'keyArns' is not provided and 'KmsKeyId' is specified in the Amazon SageMaker Endpoint Config.
- if not valid_rule_parameters:
- evaluations.append(build_evaluation(endpoint_config, 'COMPLIANT', event))
-
- #SCENARIO 5: 'KmsKeyId' specified in the Amazon SageMaker Endpoint Config matches one of the AWS KMS key IDs specified in the rule parameter 'keyArns'.
- elif sagemaker_endpoint_details['KmsKeyId'] in valid_rule_parameters:
- evaluations.append(build_evaluation(endpoint_config, 'COMPLIANT', event))
-
- #SCENARIO 4: 'KmsKeyId' specified for the Amazon SageMaker Endpoint Config is not one of the AWS KMS key IDs specified in the rule parameter 'keyArns'.
- else:
- evaluations.append(build_evaluation(endpoint_config, 'NON_COMPLIANT', event, annotation="AWS KMS Key configured for this Amazon SageMaker Endpoint Config is not an KMS Key allowed in the rule parameter (keyArns)"))
-
- #SCENARIO 3: KmsKey is not specified in the sagemaker endpoint
- else:
- evaluations.append(build_evaluation(endpoint_config, 'NON_COMPLIANT', event, annotation="No AWS KMS Key is configured for this Amazon SageMaker Endpoint Config."))
-
- return evaluations
-
-def evaluate_parameters(rule_parameters):
- valid_keyarns = []
- if 'keyArns' in rule_parameters:
- given_keyarns = rule_parameters['keyArns'].replace(' ', '').split(',')
- for given_keyid in given_keyarns:
- if re.match('arn:aws:kms:[a-z0-9-]+:[0-9]{12}:key/[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}', given_keyid):
- #SCENARIO1: The rule parameter'keyArns'is provided and is invalid.
- valid_keyarns.append(given_keyid)
- else:
- raise ValueError('The KMS Key arn should be in the right format.')
- return valid_keyarns
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event, region=None):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- region -- the region where the client is called (default: None)
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service, region)
- credentials = get_assume_role_credentials(event["executionRoleArn"], region)
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken'],
- region_name=region
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None). It will be truncated to 255 if longer.
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = build_annotation(annotation)
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None). It will be truncated to 255 if longer.
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = build_annotation(annotation)
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Build annotation within Service constraints
-def build_annotation(annotation_string):
- if len(annotation_string) > 256:
- return annotation_string[:244] + " [truncated]"
- return annotation_string
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
-
- return status in ('OK', 'ResourceDiscovered') and not event_left_scope
-
-
-def get_assume_role_credentials(role_arn, region=None):
- sts_client = boto3.client('sts', region)
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED/SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED_test.py b/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED/SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED_test.py
deleted file mode 100644
index d4f9759..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED/SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED_test.py
+++ /dev/null
@@ -1,261 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-SAGEMAKER_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 'sagemaker':
- return SAGEMAKER_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED')
-
-class ComplianceTest(unittest.TestCase):
-
- rule_parameters_scenarios = '{"keyArns":"arn:aws:kms:us-east-1:123456789012:key/ae25566a-c0d4-4ed2-8131-7c98e9487h3d, arn:aws:kms:us-east-1:123456789012:key/ae25566a-c0d4-4edg-8131-7c98e9487e3d"}'
- list_endpoints_scenarios = [{'EndpointConfigs':[{'EndpointConfigName':'endpoint1'}, {'EndpointConfigName':'endpoint2'}]}]
-
- described_endpoints_scenario3 = [{'EndpointConfigName':'endpoint1', 'EndpointConfigArn': 'arn:aws:sagemaker:us-east-1:123456789012:endpoint-config/endpoint1'}, {'EndpointConfigName': 'endpoint2', 'EndpointConfigArn': 'arn:aws:sagemaker:us-east-1:123456789012:endpoint-config/endpoint2'}]
- described_endpoints_scenario4 = [{'EndpointConfigName':'endpoint1', 'KmsKeyId': 'arn:aws:kms:us-west-1:123456789012:key/ae34566a-b0k4-4ed2-8131-7c00f1487s3d', 'EndpointConfigArn': 'arn:aws:sagemaker:us-west-1:123456789012:endpoint-config/endpoint3'}, {'EndpointConfigName': 'endpoint2', 'EndpointConfigArn': 'arn:aws:sagemaker:us-apsoutheast-1-1:123456789012:endpoint-config/endpoint2', 'KmsKeyId': 'arn:aws:kms:us-apsoutheast-1:305333957852:key/ae27766a-b0d4-4ed2-8131-7c13e9487e3d'}]
- described_endpoints_scenario5 = [{'EndpointConfigName':'endpoint1', 'KmsKeyId': 'arn:aws:kms:us-east-1:123456789012:key/ae25566a-c0d4-4ed2-8131-7c98e9487h3d', 'EndpointConfigArn': 'arn:aws:sagemaker:us-east-1:123456789012:endpoint-config/endpoint5'}, {'EndpointConfigName': 'endpoint2', 'EndpointConfigArn': 'arn:aws:sagemaker:us-east-1:123456789012:endpoint-config/endpoint2', 'KmsKeyId': 'arn:aws:kms:us-east-1:123456789012:key/ae25566a-c0d4-4edg-8131-7c98e9487e3d'}]
-
- rule_parameters_scenario6 = '{}'
- described_endpoints_scenario6 = [{'EndpointConfigName':'endpoint2', 'KmsKeyId': 'arn:aws:kms:us-east-1:123456789012:key/ae25566a-c0d4-4ed2-8131-7c98e9487e3d', 'EndpointConfigArn': 'arn:aws:sagemaker:us-east-1:123456789012:endpoint-config/endpoint2'}, {'EndpointConfigName': 'endpoint2', 'EndpointConfigArn': 'arn:aws:sagemaker:us-east-1:123456789012:endpoint-config/endpoint8', 'KmsKeyId': 'arn:aws:kms:us-east-1:123456789012:key/ae25566a-c0d4-4ed2-8131-7c98e9487e3f'}]
-
- list_endpoints_scenario7 = [{'EndpointConfigs':[{'EndpointConfigName':'endpoint1'}, {'EndpointConfigName':'endpoint2'}, {'EndpointConfigName':'endpoint3'}]}]
- described_endpoints_scenario7 = [{'EndpointConfigName':'endpoint1', 'KmsKeyId': 'arn:aws:kms:us-east-1:123456789012:key/ae25566a-c0d4-4ed2-8131-7c98e9487h3d', 'EndpointConfigArn': 'arn:aws:sagemaker:us-east-1:123456789012:endpoint-config/endpoint1'}, {'EndpointConfigName':'endpoint2', 'KmsKeyId': 'arn:aws:kms:us-east-1:123456789012:key/fd21436a-k9c0-1sj3-7225-1mnbm8170a9g', 'EndpointConfigArn': 'arn:aws:sagemaker:us-east-1:123456789012:endpoint-config/endpoint2'}, {'EndpointConfigName':'endpoint3', 'EndpointConfigArn': 'arn:aws:sagemaker:us-east-1:123456789012:endpoint-config/endpoint3'}]
-
- #Scenario 2 No Amazon SageMaker endpoint configs exist
- def test_scenario_2_no_endpoints(self):
- SAGEMAKER_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": SAGEMAKER_CLIENT_MOCK,
- "paginate.return_value": [{'EndpointConfigs': []}]})
- lambda_event = build_lambda_scheduled_event()
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', '123456789012'))
- assert_successful_evaluation(self, response, resp_expected)
-
- #Scenario 3:'KmsKeyId' is not specified for the Amazon SageMaker Endpoint Config
- def test_scenario_3_no_kms_present(self):
- SAGEMAKER_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": SAGEMAKER_CLIENT_MOCK,
- "paginate.return_value": self.list_endpoints_scenarios})
- lambda_event = build_lambda_scheduled_event(rule_parameters=self.rule_parameters_scenarios)
- SAGEMAKER_CLIENT_MOCK.describe_endpoint_config = MagicMock(side_effect=self.described_endpoints_scenario3)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'endpoint1', annotation="No AWS KMS Key is configured for this Amazon SageMaker Endpoint Config."))
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'endpoint2', annotation="No AWS KMS Key is configured for this Amazon SageMaker Endpoint Config."))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- #Scenario 4 None of the AWS KMS key IDs specified in the rule parameter 'keyArns' match 'KmsKeyId'
- def test_scenario_4_no_matching_keyids(self):
- SAGEMAKER_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": SAGEMAKER_CLIENT_MOCK,
- "paginate.return_value": self.list_endpoints_scenarios})
- lambda_event = build_lambda_scheduled_event(rule_parameters=self.rule_parameters_scenarios)
- SAGEMAKER_CLIENT_MOCK.describe_endpoint_config = MagicMock(side_effect=self.described_endpoints_scenario4)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'endpoint1', annotation="AWS KMS Key configured for this Amazon SageMaker Endpoint Config is not an KMS Key allowed in the rule parameter (keyArns)"))
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'endpoint2', annotation="AWS KMS Key configured for this Amazon SageMaker Endpoint Config is not an KMS Key allowed in the rule parameter (keyArns)"))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- #Scenario 5: 'KmsKeyId' is specified in the Amazon SageMaker Endpoint Config
- def test_scenario_5_compliant(self):
- SAGEMAKER_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": SAGEMAKER_CLIENT_MOCK,
- "paginate.return_value": self.list_endpoints_scenarios})
- lambda_event = build_lambda_scheduled_event(rule_parameters=self.rule_parameters_scenarios)
- SAGEMAKER_CLIENT_MOCK.describe_endpoint_config = MagicMock(side_effect=self.described_endpoints_scenario5)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'endpoint1'))
- resp_expected.append(build_expected_response('COMPLIANT', 'endpoint2'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- #Scenario6: The rule parameter 'keyArns' is not provided
- def test_scenario_6_compliant(self):
- SAGEMAKER_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": SAGEMAKER_CLIENT_MOCK,
- "paginate.return_value": self.list_endpoints_scenarios})
- SAGEMAKER_CLIENT_MOCK.describe_endpoint_config = MagicMock(side_effect=self.described_endpoints_scenario6)
- lambda_event = build_lambda_scheduled_event(rule_parameters=self.rule_parameters_scenario6)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'endpoint1'))
- resp_expected.append(build_expected_response('COMPLIANT', 'endpoint2'))
- assert_successful_evaluation(self, response, resp_expected, 2)
-
- def test_scenario7_mix(self):
- SAGEMAKER_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value":SAGEMAKER_CLIENT_MOCK,
- "paginate.return_value":self.list_endpoints_scenario7
- })
- SAGEMAKER_CLIENT_MOCK.describe_endpoint_config = MagicMock(side_effect=self.described_endpoints_scenario7)
- lambda_event = build_lambda_scheduled_event(rule_parameters=self.rule_parameters_scenarios)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'endpoint1'))
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'endpoint2', annotation="AWS KMS Key configured for this Amazon SageMaker Endpoint Config is not an KMS Key allowed in the rule parameter (keyArns)"))
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'endpoint3', annotation="No AWS KMS Key is configured for this Amazon SageMaker Endpoint Config."))
- assert_successful_evaluation(self, response, resp_expected, 3)
-
-class ParametersTest(unittest.TestCase):
-
- rule_parameters = '{"keyArns":"arn:aws:kms:us-east-1:123456789012:key/ae25566a-c0d4-4ed2-8131-7c98e9487e3d, arn:als:kms:us-east-1:123456789012:keys/ae25566a-c0d4-4ed2-8131-7c98e9487e3d"}'
-
- #Scenario1: The rule parameter 'keyArns' is provided and is invalid
- def test_scenario1(self):
- lambda_event = build_lambda_scheduled_event(rule_parameters=self.rule_parameters)
- response = RULE.lambda_handler(lambda_event, {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException', customer_error_message='The KMS Key arn should be in the right format.')
-
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- RULE.evaluate_parameters = MagicMock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- RULE.evaluate_parameters = MagicMock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED/parameters.json
deleted file mode 100644
index 3c18f3d..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED",
- "SourceRuntime": "python3.6",
- "CodeKey": "SAGEMAKER_ENDPOINT_CONFIG_KMS_KEY_CONFIGURED.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{\"keyArns\": \"\"}",
- "SourcePeriodic": "TwentyFour_Hours"
- },
- "Tags": "[]"
-}
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_KMS_CONFIGURED/SAGEMAKER_NOTEBOOK_KMS_CONFIGURED.py b/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_KMS_CONFIGURED/SAGEMAKER_NOTEBOOK_KMS_CONFIGURED.py
deleted file mode 100644
index 43a7269..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_KMS_CONFIGURED/SAGEMAKER_NOTEBOOK_KMS_CONFIGURED.py
+++ /dev/null
@@ -1,445 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-"""
-#####################################
-## Gherkin ##
-#####################################
-Rule Name:
- SAGEMAKER_NOTEBOOK_KMS_CONFIGURED
-
-Description:
- Check whether an AWS KMS key is configured for an Amazon SageMaker Notebook Instance.
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::SageMaker::NotebookInstance
-
-Rule Parameters:
- keyArns
- (Optional) Comma-separated list of allowed AWS KMS key IDs.
-
-Scenarios:
- Scenario: 1
- Given: The rule parameter 'keyArns' is provided and is invalid
- Then: Return ERROR
- Scenario: 2
- Given: No Amazon SageMaker notebook instances exist
- Then: Return NOT_APPLICABLE
- Scenario: 3
- Given: At least one Amazon SageMaker notebook instance exists
- And: 'KmsKeyId' is not specified for the Amazon SageMaker Notebook Instance
- Then: Return NON_COMPLIANT with annotation "No AWS KMS Key is configured for this Amazon SageMaker Notebook Instance."
- Scenario: 4
- Given: The rule parameter 'keyArns' is provided and is valid
- And: At least one Amazon SageMaker notebook instance exists
- And: 'KmsKeyId' is specified for the Amazon SageMaker Notebook Instance
- And: None of the AWS KMS key IDs specified in the rule parameter 'keyArns' match 'KmsKeyId'
- Then: Return NON_COMPLIANT with annotation "The AWS KMS Key configured for this Amazon SageMaker Notebook Instance is not an KMS Key allowed in the rule parameter (keyArns)."
- Scenario: 5
- Given: The rule parameter 'keyArns' is provided and is valid
- And: At least one Amazon SageMaker notebook instance exists
- And: 'KmsKeyId' is specified in the Amazon SageMaker Notebook Instance
- And: One of the AWS KMS key IDs specified in the rule parameter 'keyArns' matches 'KmsKeyId'
- Then: Return COMPLIANT
- Scenario: 6
- Given: The rule parameter 'keyArns' is not provided
- And: At least one Amazon SageMaker notebook instance exists
- And: 'KmsKeyId' is specified for the Amazon SageMaker Notebook Instance
- Then: Return COMPLIANT
-"""
-
-import json
-import sys
-import datetime
-import re
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- evaluations = []
-
- sagemaker_client = get_client('sagemaker', event)
- notebook_instances_list = get_all_notebook_instances(sagemaker_client)
-
- #SCENARIO 2: No Amazon SageMaker Notebook Instances.
- if not notebook_instances_list:
- return None
-
- for notebook_instance in notebook_instances_list:
- notebook_instance_description = sagemaker_client.describe_notebook_instance(NotebookInstanceName=notebook_instance['NotebookInstanceName'])
- #SCENARIO 3: KMS key not specified for the Amazon SageMaker Notebook Instance.
- if 'KmsKeyId' not in notebook_instance_description:
- evaluations.append(build_evaluation(notebook_instance['NotebookInstanceName'], 'NON_COMPLIANT', event, annotation='No AWS KMS Key is configured for this Amazon SageMaker Notebook Instance.'))
- #SCENARIO 6: KMS key specified for Amazon SageMaker Notebook Instance but no rule parameter provided.
- elif not valid_rule_parameters:
- evaluations.append(build_evaluation(notebook_instance['NotebookInstanceName'], 'COMPLIANT', event))
- #SCENARIO 5: KMS key specified for Amazon SageMaker Notebook Instance matches keyArn in rule parameter.
- elif notebook_instance_description['KmsKeyId'] in valid_rule_parameters:
- evaluations.append(build_evaluation(notebook_instance['NotebookInstanceName'], 'COMPLIANT', event))
- #SCENARIO 4: KMS key specified for Amazon SageMaker Notebook Instance does not match keyArn in rule parameter.
- else:
- evaluations.append(build_evaluation(notebook_instance['NotebookInstanceName'], 'NON_COMPLIANT', event, annotation='The AWS KMS Key configured for this Amazon SageMaker Notebook Instance is not an KMS Key allowed in the rule parameter (keyArns).'))
- return evaluations
-
-def get_all_notebook_instances(sagemaker_client):
- notebook_instances_list = []
- paginator = sagemaker_client.get_paginator('list_notebook_instances')
- page_iterator = paginator.paginate()
- for page in page_iterator:
- for notebook_instance in page['NotebookInstances']:
- notebook_instances_list.append(notebook_instance)
- return notebook_instances_list
-
-def evaluate_parameters(rule_parameters):
- valid_rule_parameters = []
- if "keyArns" in rule_parameters:
- keys = [rule_parameter.strip() for rule_parameter in rule_parameters['keyArns'].split(',')]
- regex_pattern = re.compile("arn:aws:kms:[a-z0-9-]+:[0-9]{12}:key/[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}")
- for key in keys:
- if regex_pattern.match(key):
- valid_rule_parameters.append(key)
- else:
- #SCENARIO 1: Invalid parameter
- raise ValueError('The KMS Key ARN should be in the right format.')
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event, region=None):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- region -- the region where the client is called (default: None)
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service, region)
- credentials = get_assume_role_credentials(event["executionRoleArn"], region)
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken'],
- region_name=region
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None). It will be truncated to 255 if longer.
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = build_annotation(annotation)
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None). It will be truncated to 255 if longer.
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = build_annotation(annotation)
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Build annotation within Service constraints
-def build_annotation(annotation_string):
- if len(annotation_string) > 256:
- return annotation_string[:244] + " [truncated]"
- return annotation_string
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
-
- return status in ('OK', 'ResourceDiscovered') and not event_left_scope
-
-
-def get_assume_role_credentials(role_arn, region=None):
- sts_client = boto3.client('sts', region)
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_KMS_CONFIGURED/SAGEMAKER_NOTEBOOK_KMS_CONFIGURED_test.py b/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_KMS_CONFIGURED/SAGEMAKER_NOTEBOOK_KMS_CONFIGURED_test.py
deleted file mode 100644
index afec18a..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_KMS_CONFIGURED/SAGEMAKER_NOTEBOOK_KMS_CONFIGURED_test.py
+++ /dev/null
@@ -1,250 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-SAGEMAKER_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 'sagemaker':
- return SAGEMAKER_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('SAGEMAKER_NOTEBOOK_KMS_CONFIGURED')
-
-class ComplianceTest(unittest.TestCase):
-
- notebook_instances_list = {'NotebookInstances': [{'NotebookInstanceName': 'trial12'}, {'NotebookInstanceName': 'trial123'}]}
- no_notebook_instances_list = {"NotebookInstances": []}
- notebook_instances_list_mixed = [{'NotebookInstances': [{'NotebookInstanceName': 'trial12'}, {'NotebookInstanceName': 'trial123'}, {'NotebookInstanceName': 'trial1234'}]}]
-
- described_notebook_instances = [{'NotebookInstanceName': 'trial12', 'KmsKeyId': 'arn:aws:kms:us-east-1:123456789012:key/7af97db7-f6a3-4d0a-87b9-a2737b54856d'}, {'NotebookInstanceName': 'trial123', 'KmsKeyId': 'arn:aws:kms:us-east-1:123456789012:key/7af97db7-f6a3-4d0a-87b9-a2737b54856d'}]
- described_notebooks_no_key = [{'NotebookInstanceName': 'trial12'}, {'NotebookInstanceName': 'trial123'}]
- described_notebooks_mixed = [{'NotebookInstanceName': 'trial12', 'KmsKeyId': 'arn:aws:kms:us-east-1:123456789012:key/7af97db7-f6a3-4d0a-87b9-a2737b54856d'}, {'NotebookInstanceName': 'trial123', 'KmsKeyId': 'arn:aws:kms:us-east-1:123456789012:key/7af97db6-f6a3-4d0a-87b9-a2737b54856d'}, {'NotebookInstanceName': 'trial1234'}]
-
- rule_params_mismatched_key = '{"keyArns":"arn:aws:kms:us-east-1:123456789012:key/7af97db6-f6a3-4d0a-87b9-a2737b54856d, arn:aws:kms:us-east-1:123456789012:key/7af97db6-f6a3-4d0a-87b9-a2737b54856e"}'
- rule_params_matched_key = '{"keyArns":"arn:aws:kms:us-east-1:123456789012:key/7af97db7-f6a3-4d0a-87b9-a2737b54856d, arn:aws:kms:us-east-1:123456789012:key/7af97db7-f6a3-4d0a-87b9-a2737b54856e"}'
-
- #SCENARIO 2: No Amazon SageMaker Notebook Instances.
- def test_scenario_2_no_instance(self):
- SAGEMAKER_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": SAGEMAKER_CLIENT_MOCK,
- "paginate.return_value": [self.no_notebook_instances_list]})
- lambda_event = build_lambda_scheduled_event()
- response = RULE.lambda_handler(lambda_event, {})
- expected_response = [build_expected_response('NOT_APPLICABLE', '123456789012', 'AWS::::Account')]
- assert_successful_evaluation(self, response, expected_response)
-
- #SCENARIO 3: KMS key not specified for the Amazon SageMaker Notebook Instance.
- def test_scenario_3_no_keys(self):
- SAGEMAKER_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": SAGEMAKER_CLIENT_MOCK,
- "paginate.return_value": [self.notebook_instances_list]})
- SAGEMAKER_CLIENT_MOCK.describe_notebook_instance = MagicMock(side_effect=self.described_notebooks_no_key)
- lambda_event = build_lambda_scheduled_event()
- response = RULE.lambda_handler(lambda_event, {})
- expected_response = [build_expected_response('NON_COMPLIANT', 'trial12', annotation='No AWS KMS Key is configured for this Amazon SageMaker Notebook Instance.'),
- build_expected_response('NON_COMPLIANT', 'trial123', annotation='No AWS KMS Key is configured for this Amazon SageMaker Notebook Instance.')]
- assert_successful_evaluation(self, response, expected_response, evaluations_count=2)
-
- #SCENARIO 4: KMS key specified for Amazon SageMaker Notebook Instance does not match keyArn in rule parameter.
- def test_scenario_4_no_match_keys(self):
- SAGEMAKER_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": SAGEMAKER_CLIENT_MOCK,
- "paginate.return_value": [self.notebook_instances_list]})
- SAGEMAKER_CLIENT_MOCK.describe_notebook_instance = MagicMock(side_effect=self.described_notebook_instances)
- lambda_event = build_lambda_scheduled_event(rule_parameters=self.rule_params_mismatched_key)
- response = RULE.lambda_handler(lambda_event, {})
- expected_response = [build_expected_response('NON_COMPLIANT', 'trial12', annotation='The AWS KMS Key configured for this Amazon SageMaker Notebook Instance is not an KMS Key allowed in the rule parameter (keyArns).'),
- build_expected_response('NON_COMPLIANT', 'trial123', annotation='The AWS KMS Key configured for this Amazon SageMaker Notebook Instance is not an KMS Key allowed in the rule parameter (keyArns).')]
- assert_successful_evaluation(self, response, expected_response, evaluations_count=2)
-
- #SCENARIO 5: KMS key specified for Amazon SageMaker Notebook Instance matches keyArn in rule parameter.
- def test_scenario_5_compliant(self):
- SAGEMAKER_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": SAGEMAKER_CLIENT_MOCK,
- "paginate.return_value": [self.notebook_instances_list]})
- SAGEMAKER_CLIENT_MOCK.describe_notebook_instance = MagicMock(side_effect=self.described_notebook_instances)
- lambda_event = build_lambda_scheduled_event(rule_parameters=self.rule_params_matched_key)
- response = RULE.lambda_handler(lambda_event, {})
- expected_response = [build_expected_response('COMPLIANT', 'trial12'),
- build_expected_response('COMPLIANT', 'trial123')]
- assert_successful_evaluation(self, response, expected_response, evaluations_count=2)
-
- #SCENARIO 6: KMS key specified for Amazon SageMaker Notebook Instance but no rule parameter provided.
- def test_scenerio_6_no_param_compliant(self):
- SAGEMAKER_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": SAGEMAKER_CLIENT_MOCK,
- "paginate.return_value": [self.notebook_instances_list]})
- SAGEMAKER_CLIENT_MOCK.describe_notebook_instance = MagicMock(side_effect=self.described_notebook_instances)
- lambda_event = build_lambda_scheduled_event()
- response = RULE.lambda_handler(lambda_event, {})
- expected_response = [build_expected_response('COMPLIANT', 'trial12'),
- build_expected_response('COMPLIANT', 'trial123')]
- assert_successful_evaluation(self, response, expected_response, evaluations_count=2)
-
- def test_scenario_7_mixed(self):
- SAGEMAKER_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": SAGEMAKER_CLIENT_MOCK,
- "paginate.return_value": self.notebook_instances_list_mixed})
- SAGEMAKER_CLIENT_MOCK.describe_notebook_instance = MagicMock(side_effect=self.described_notebooks_mixed)
- lambda_event = build_lambda_scheduled_event(rule_parameters=self.rule_params_matched_key)
- response = RULE.lambda_handler(lambda_event, {})
- expected_response = [build_expected_response('COMPLIANT', 'trial12'),
- build_expected_response('NON_COMPLIANT', 'trial123', annotation='The AWS KMS Key configured for this Amazon SageMaker Notebook Instance is not an KMS Key allowed in the rule parameter (keyArns).'),
- build_expected_response('NON_COMPLIANT', 'trial1234', annotation='No AWS KMS Key is configured for this Amazon SageMaker Notebook Instance.')]
- assert_successful_evaluation(self, response, expected_response, evaluations_count=3)
-
-class ParameterTest(unittest.TestCase):
-
- #SCENARIO 1: Invalid parameter
- def test_scenario_1_invalid_param(self):
- rule_param = '{"keyArns": "83de41d66530-49c1-9cb7-1de1560ce5tg"}'
- lambda_event = build_lambda_scheduled_event(rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException', 'The KMS Key ARN should be in the right format.')
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- RULE.evaluate_parameters = MagicMock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- RULE.evaluate_parameters = MagicMock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_KMS_CONFIGURED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_KMS_CONFIGURED/parameters.json
deleted file mode 100644
index 9d841f5..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_KMS_CONFIGURED/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "SAGEMAKER_NOTEBOOK_KMS_CONFIGURED",
- "SourceRuntime": "python3.6",
- "CodeKey": "SAGEMAKER_NOTEBOOK_KMS_CONFIGURED.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{\"keyArns\": \"\"}",
- "SourcePeriodic": "TwentyFour_Hours"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS/SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS.py b/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS/SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS.py
deleted file mode 100644
index d735848..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS/SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS.py
+++ /dev/null
@@ -1,410 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-'''
-#####################################
-## Gherkin ##
-#####################################
-Rule Name:
- SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS
-
-Description:
- Check whether direct internet access is disabled for an Amazon SageMaker Notebook Instance.
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::::Account
-
-Scenarios:
- Scenario: 1
- Given: No Amazon SageMaker notebook instances exist
- Then: Return NOT_APPLICABLE
- Scenario: 2
- Given: At least one Amazon SageMaker notebook instance exists
- And: DirectInternetAccess is set to Enabled for the Amazon SageMaker notebook instance
- Then: Return NON_COMPLIANT with annotation "This Amazon SageMaker Notebook Instance has direct internet access."
- Scenario: 3
- Given: At least one Amazon SageMaker notebook instance exists
- And: DirectInternetAccess is set to Disabled for the Amazon SageMaker notebook instance
- Then: Return COMPLIANT
-'''
-
-import json
-import sys
-import datetime
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- evaluations = []
-
- sagemaker_client = get_client('sagemaker', event)
- notebook_instance_list = get_all_notebook_instances(sagemaker_client)
-
- #SCENARIO 1: No Amazon SageMaker notebook instances exist
- if not notebook_instance_list:
- return None
-
- for notebook_instance in notebook_instance_list:
- notebook_instance_description = sagemaker_client.describe_notebook_instance(NotebookInstanceName=notebook_instance['NotebookInstanceName'])
- #SCENARIO 2: DirectInternetAccess is set to Enabled for the Amazon SageMaker notebook instance
- if notebook_instance_description['DirectInternetAccess'] == 'Enabled':
- evaluations.append(build_evaluation(notebook_instance['NotebookInstanceName'], 'NON_COMPLIANT', event, annotation="This Amazon SageMaker Notebook Instance has direct internet access."))
- #SCENARIO 3: DirectInternetAccess is set to Disabled for the Amazon SageMaker notebook instance
- else:
- evaluations.append(build_evaluation(notebook_instance['NotebookInstanceName'], 'COMPLIANT', event))
- return evaluations
-
-#Returns a list of all the SageMaker notebook instances by making use of Boto3's pagination methods.
-def get_all_notebook_instances(sagemaker_client):
- notebook_instance_list = []
- paginator = sagemaker_client.get_paginator('list_notebook_instances')
- page_iterator = paginator.paginate()
- for page in page_iterator:
- for notebook_instance in page['NotebookInstances']:
- notebook_instance_list.append(notebook_instance)
- return notebook_instance_list
-
-def evaluate_parameters(rule_parameters):
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event, region=None):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- region -- the region where the client is called (default: None)
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service, region)
- credentials = get_assume_role_credentials(event["executionRoleArn"], region)
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken'],
- region_name=region
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None). It will be truncated to 255 if longer.
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = build_annotation(annotation)
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None). It will be truncated to 255 if longer.
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = build_annotation(annotation)
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Build annotation within Service constraints
-def build_annotation(annotation_string):
- if len(annotation_string) > 256:
- return annotation_string[:244] + " [truncated]"
- return annotation_string
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
-
- return status in ('OK', 'ResourceDiscovered') and not event_left_scope
-
-
-def get_assume_role_credentials(role_arn, region=None):
- sts_client = boto3.client('sts', region)
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS/SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS_test.py b/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS/SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS_test.py
deleted file mode 100644
index 54cd06c..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS/SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS_test.py
+++ /dev/null
@@ -1,190 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-SAGEMAKER_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 'sagemaker':
- return SAGEMAKER_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS')
-
-class ComplianceTest(unittest.TestCase):
-
- notebook_instances_list = [{'NotebookInstances': [{'NotebookInstanceName': 'trial12'}, {'NotebookInstanceName': 'trial123'}]}]
- notebooks_direct_internet = [{'NotebookInstanceName': 'trial12', 'DirectInternetAccess': 'Enabled'}, {'NotebookInstanceName': 'trial123', 'DirectInternetAccess': 'Enabled'}]
- notebooks_no_direct_internet = [{'NotebookInstanceName': 'trial12', 'DirectInternetAccess': 'Disabled'}, {'NotebookInstanceName': 'trial123', 'DirectInternetAccess': 'Disabled'}]
- notebooks_both = [{'NotebookInstanceName': 'trial12', 'DirectInternetAccess': 'Disabled'}, {'NotebookInstanceName': 'trial123', 'DirectInternetAccess': 'Enabled'}]
-
- #SCENARIO 1: No Amazon SageMaker notebook instances exist
- def test_scenario_1_no_notebooks(self):
- notebook_instances_list = [{'NotebookInstances': []}]
- RULE.ASSUME_ROLE_MODE = False
- SAGEMAKER_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": SAGEMAKER_CLIENT_MOCK,
- "paginate.return_value": notebook_instances_list})
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = [build_expected_response('NOT_APPLICABLE', '123456789012', 'AWS::::Account')]
- assert_successful_evaluation(self, response, resp_expected)
-
- #SCENARIO 2: DirectInternetAccess is set to Enabled for the Amazon SageMaker notebook instances
- def test_scenario_2_direct_internet_access(self):
- RULE.ASSUME_ROLE_MODE = False
- annotation = "This Amazon SageMaker Notebook Instance has direct internet access."
- SAGEMAKER_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": SAGEMAKER_CLIENT_MOCK,
- "paginate.return_value": self.notebook_instances_list})
- SAGEMAKER_CLIENT_MOCK.describe_notebook_instance = MagicMock(side_effect=self.notebooks_direct_internet)
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = [build_expected_response('NON_COMPLIANT', compliance_resource_id='trial12', annotation=annotation),
- build_expected_response('NON_COMPLIANT', compliance_resource_id='trial123', annotation=annotation)]
- assert_successful_evaluation(self, response, resp_expected, evaluations_count=2)
-
- #SCENARIO 3: DirectInternetAccess is set to Disabled for the Amazon SageMaker notebook instances
- def test_scenario_3_no_direct_internet_access(self):
- RULE.ASSUME_ROLE_MODE = False
- SAGEMAKER_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": SAGEMAKER_CLIENT_MOCK,
- "paginate.return_value": self.notebook_instances_list})
- SAGEMAKER_CLIENT_MOCK.describe_notebook_instance = MagicMock(side_effect=self.notebooks_no_direct_internet)
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = [build_expected_response('COMPLIANT', compliance_resource_id='trial12'),
- build_expected_response('COMPLIANT', compliance_resource_id='trial123')]
- assert_successful_evaluation(self, response, resp_expected, evaluations_count=2)
-
- #Test for a mix of compliance types
- def test_scenario_2_and_3(self):
- RULE.ASSUME_ROLE_MODE = False
- annotation = "This Amazon SageMaker Notebook Instance has direct internet access."
- SAGEMAKER_CLIENT_MOCK.configure_mock(**{
- "get_paginator.return_value": SAGEMAKER_CLIENT_MOCK,
- "paginate.return_value": self.notebook_instances_list})
- SAGEMAKER_CLIENT_MOCK.describe_notebook_instance = MagicMock(side_effect=self.notebooks_both)
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = [build_expected_response('COMPLIANT', compliance_resource_id='trial12'),
- build_expected_response('NON_COMPLIANT', compliance_resource_id='trial123', annotation=annotation)]
- assert_successful_evaluation(self, response, resp_expected, evaluations_count=2)
-
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS/parameters.json
deleted file mode 100644
index d113d40..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS",
- "SourceRuntime": "python3.6",
- "CodeKey": "SAGEMAKER_NOTEBOOK_NO_DIRECT_INTERNET_ACCESS.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourcePeriodic": "TwentyFour_Hours"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SECRETSMANAGER_MAX_SECRET_AGE/SECRETSMANAGER_MAX_SECRET_AGE.py b/vendor/github.com/awslabs/aws-config-rules/python/SECRETSMANAGER_MAX_SECRET_AGE/SECRETSMANAGER_MAX_SECRET_AGE.py
deleted file mode 100644
index 17c9dbd..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SECRETSMANAGER_MAX_SECRET_AGE/SECRETSMANAGER_MAX_SECRET_AGE.py
+++ /dev/null
@@ -1,434 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-import json
-import sys
-import datetime
-from datetime import datetime, timedelta, timezone
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::SecretsManager::Secret'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-# Default age of SecretValue if max_secret_age_days is not provided in Rule Parameters
-# Parameter must be a positive integer less than 999999999+1
-DEFAULT_MAX_SECRET_AGE_DAYS = 30
-
-#############
-# Main Code #
-#############
-
-
-def evaluate_secret_compliance(valid_rule_parameters, secret):
- now = datetime.now(timezone.utc)
- delta = timedelta(days=valid_rule_parameters.get('max_secret_age_days'))
- max_secret_age = now - delta
-
- if secret.get('LastRotatedDate'):
- if datetime.replace(secret.get('LastRotatedDate'), tzinfo=timezone.utc) > max_secret_age:
- return 'COMPLIANT'
- return 'NON_COMPLIANT'
-
- # Pagination of this API call is not needed as this API is only called if Secret has never been rotated
- # This should always return only a single VersionId with VersionLabel AWSCURRENT
- secret_versions = AWS_SECRETSMANAGER_CLIENT.list_secret_version_ids(
- SecretId=secret.get('Name'),
- IncludeDeprecated=False
- ).get('Versions')
-
- # Secret conains no SecretValues
- if not secret_versions:
- return 'COMPLIANT'
-
- for version in secret_versions:
- if 'AWSCURRENT' in version.get('VersionStages'):
- if datetime.replace(version.get('CreatedDate'), tzinfo=timezone.utc) > max_secret_age:
- return 'COMPLIANT'
- return 'NON_COMPLIANT'
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
-
- ###############################
- # Add your custom logic here. #
- ###############################
-
- evaluations = []
- paginator = AWS_SECRETSMANAGER_CLIENT.get_paginator('list_secrets')
-
- for secret_list in paginator.paginate():
- for secret in secret_list['SecretList']:
- secret_arn = secret.get('ARN')
- evaluations.append(build_evaluation(secret_arn, evaluate_secret_compliance(valid_rule_parameters, secret), event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None))
-
- return evaluations
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
-
- try:
- max_secret_age_days = int(rule_parameters.get('max_secret_age_days', DEFAULT_MAX_SECRET_AGE_DAYS))
- except TypeError:
- raise ValueError('max_secret_age_days must be an integer')
-
- if max_secret_age_days > timedelta.max.days:
- raise ValueError('max_secret_age_days must be less than ' + str(timedelta.max.days))
-
- return {"max_secret_age_days": max_secret_age_days}
-
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event, region=None):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- region -- the region where the client is called (default: None)
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service, region)
- credentials = get_assume_role_credentials(event["executionRoleArn"], region)
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken'],
- region_name=region
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None). It will be truncated to 255 if longer.
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = build_annotation(annotation)
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None). It will be truncated to 255 if longer.
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = build_annotation(annotation)
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-# Build annotation within Service constraints
-def build_annotation(annotation_string):
- if len(annotation_string) > 256:
- return annotation_string[:244] + " [truncated]"
- return annotation_string
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return status in ('OK', 'ResourceDiscovered') and not event_left_scope
-
-def get_assume_role_credentials(role_arn, region=None):
- sts_client = boto3.client('sts', region)
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
- global AWS_SECRETSMANAGER_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- AWS_SECRETSMANAGER_CLIENT = get_client('secretsmanager', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SECRETSMANAGER_MAX_SECRET_AGE/SECRETSMANAGER_MAX_SECRET_AGE_test.py b/vendor/github.com/awslabs/aws-config-rules/python/SECRETSMANAGER_MAX_SECRET_AGE/SECRETSMANAGER_MAX_SECRET_AGE_test.py
deleted file mode 100644
index 4269b9a..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SECRETSMANAGER_MAX_SECRET_AGE/SECRETSMANAGER_MAX_SECRET_AGE_test.py
+++ /dev/null
@@ -1,174 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('<%RuleName%>')
-
-class ComplianceTest(unittest.TestCase):
-
- rule_parameters = '{"SomeParameterKey":"SomeParameterValue","SomeParameterKey2":"SomeParameterValue2"}'
-
- invoking_event_iam_role_sample = '{"configurationItem":{"relatedEvents":[],"relationships":[],"configuration":{},"tags":{},"configurationItemCaptureTime":"2018-07-02T03:37:52.418Z","awsAccountId":"123456789012","configurationItemStatus":"ResourceDiscovered","resourceType":"AWS::IAM::Role","resourceId":"some-resource-id","resourceName":"some-resource-name","ARN":"some-arn"},"notificationCreationTime":"2018-07-02T23:05:34.445Z","messageType":"ConfigurationItemChangeNotification"}'
-
- def setUp(self):
- pass
-
-# def test_sample(self):
-# self.assertTrue(True)
-
- #def test_sample_2(self):
- # RULE.ASSUME_ROLE_MODE = False
- # response = RULE.lambda_handler(build_lambda_configurationchange_event(self.invoking_event_iam_role_sample, self.rule_parameters), {})
- # resp_expected = []
- # resp_expected.append(build_expected_response('NOT_APPLICABLE', 'some-resource-id', 'AWS::IAM::Role'))
- # assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- RULE.evaluate_parameters = MagicMock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- RULE.evaluate_parameters = MagicMock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SECRETSMANAGER_MAX_SECRET_AGE/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/SECRETSMANAGER_MAX_SECRET_AGE/parameters.json
deleted file mode 100644
index 22b38b2..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SECRETSMANAGER_MAX_SECRET_AGE/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "SECRETSMANAGER_MAX_SECRET_AGE",
- "SourceRuntime": "python3.7",
- "CodeKey": "SECRETSMANAGER_MAX_SECRET_AGE.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{\"max_secret_age_days\": \"30\"}",
- "SourcePeriodic": "TwentyFour_Hours"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_ADVANCED_ENABLED_AUTORENEW/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_ADVANCED_ENABLED_AUTORENEW/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_ADVANCED_ENABLED_AUTORENEW/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_ADVANCED_ENABLED_AUTORENEW/SHIELD_ADVANCED_ENABLED_AUTORENEW.py b/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_ADVANCED_ENABLED_AUTORENEW/SHIELD_ADVANCED_ENABLED_AUTORENEW.py
deleted file mode 100644
index d76efba..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_ADVANCED_ENABLED_AUTORENEW/SHIELD_ADVANCED_ENABLED_AUTORENEW.py
+++ /dev/null
@@ -1,389 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-'''
-#####################################
-## Gherkin ##
-#####################################
-Rule Name:
- SHIELD_ADVANCED_ENABLED_AUTORENEW
-
-Description:
- Check whether AWS Shield Advanced is enabled and is set to autorenew. As the API Endpoint of this service is only available in us-east-1, this rule should only be scheduled to run in us-east-1 region.
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::::Account
-
-Scenarios:
- Scenario: 1
- Given: AWS Shield Advanced is not enabled
- Then: Return NON_COMPLIANT with annotation "AWS Shield Advanced is not enabled."
- Scenario: 2
- Given: AWS Shield Advanced is enabled
- And: Autorenew is DISABLED
- Then: Return NON_COMPLIANT with annotation "AWS Shield Advanced subscription is not set to Autorenew."
- Scenario: 3
- Given: AWS Shield Advanced is enabled
- And: Autorenew is ENABLED
- Then: Return COMPLIANT
-'''
-import json
-import sys
-import datetime
-import boto3
-import botocore
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def shield_advanced_check(client):
- try:
- return client.describe_subscription()['Subscription']['AutoRenew']
- except botocore.exceptions.ClientError as error:
- return error.response['Error']['Code']
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- shield_advanced_enabled = shield_advanced_check(get_client('shield', event))
- if shield_advanced_enabled == 'ResourceNotFoundException':
- return build_evaluation(event['accountId'],
- 'NON_COMPLIANT',
- event,
- annotation='AWS Shield Advanced is not enabled.')
- if shield_advanced_enabled == 'DISABLED':
- return build_evaluation(event['accountId'],
- 'NON_COMPLIANT',
- event,
- annotation='AWS Shield Advanced subscription is not set to Autorenew.')
- return build_evaluation(event['accountId'],
- 'COMPLIANT',
- event)
-
-def evaluate_parameters(rule_parameters):
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return status in ('OK', 'ResourceDiscovered') and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_ADVANCED_ENABLED_AUTORENEW/SHIELD_ADVANCED_ENABLED_AUTORENEW_test.py b/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_ADVANCED_ENABLED_AUTORENEW/SHIELD_ADVANCED_ENABLED_AUTORENEW_test.py
deleted file mode 100644
index 876ecfa..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_ADVANCED_ENABLED_AUTORENEW/SHIELD_ADVANCED_ENABLED_AUTORENEW_test.py
+++ /dev/null
@@ -1,178 +0,0 @@
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-SHIELD_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 'shield':
- return SHIELD_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('SHIELD_ADVANCED_ENABLED_AUTORENEW')
-
-class NonCompliantTest(unittest.TestCase):
- def test_scenario_1(self):
- SHIELD_CLIENT_MOCK.describe_subscription = MagicMock(side_effect=
- botocore.exceptions.ClientError(
- {'Error': {'Code': 'ResourceNotFoundException',
- 'Message': 'The subscription does not exist.'}},
- 'operation'))
- lambda_result = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- assert_successful_evaluation(self,
- lambda_result,
- [build_expected_response('NON_COMPLIANT',
- '123456789012',
- annotation='AWS Shield Advanced is not enabled.')])
-
- def test_scenario_2(self):
- SHIELD_CLIENT_MOCK.describe_subscription = MagicMock(return_value={'Subscription':{'AutoRenew':'DISABLED'}})
- lambda_result = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- assert_successful_evaluation(self,
- lambda_result,
- [build_expected_response('NON_COMPLIANT',
- '123456789012',
- annotation=
- 'AWS Shield Advanced subscription is not set to Autorenew.')])
-
-class CompliantTest(unittest.TestCase):
- def test_scenario_3(self):
- SHIELD_CLIENT_MOCK.describe_subscription = MagicMock(return_value={'Subscription':{'AutoRenew':'ENABLED'}})
- lambda_result = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- assert_successful_evaluation(self,
- lambda_result,
- [build_expected_response('COMPLIANT',
- '123456789012')])
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_ADVANCED_ENABLED_AUTORENEW/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_ADVANCED_ENABLED_AUTORENEW/parameters.json
deleted file mode 100644
index 4808d93..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_ADVANCED_ENABLED_AUTORENEW/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "SHIELD_ADVANCED_ENABLED_AUTORENEW",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "SHIELD_ADVANCED_ENABLED_AUTORENEW"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_DRT_ACCESS/SHIELD_DRT_ACCESS.py b/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_DRT_ACCESS/SHIELD_DRT_ACCESS.py
deleted file mode 100644
index 34f2050..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_DRT_ACCESS/SHIELD_DRT_ACCESS.py
+++ /dev/null
@@ -1,386 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-"""
-Rule Name:
- SHIELD_DRT_ACCESS
-
-Description:
- Verify that DRT can access the AWS account. As the API Endpoint of this service is only available in us-east-1, this rule should only be scheduled to run in us-east-1 region.
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::::Account
-
-Scenarios:
- Scenario: 1
- Given: AWS Shield Advanced is not enabled
- Then: Return NOT_APPLICABLE
- Scenario: 2
- Given: AWS Shield Advanced is enabled
- And: Role for DRT access is not configured
- Then: Return NON_COMPLIANT with annotation "DRT team does not have access to account."
- Scenario: 3
- Given: AWS Shield Advanced is enabled
- And: Role for DRT access is configured
- Then: Return COMPLIANT
-"""
-
-import json
-import sys
-import datetime
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-def drt_access_check(client):
- try:
- return client.describe_drt_access()
- except botocore.exceptions.ClientError as error:
- return error.response['Error']['Code']
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- drt_access_enabled = drt_access_check(get_client('shield', event))
- if drt_access_enabled == 'ResourceNotFoundException':
- return build_evaluation(event['accountId'],
- 'NOT_APPLICABLE',
- event,
- annotation='AWS Shield Advanced is not enabled.')
- if 'RoleArn' in drt_access_enabled:
- return build_evaluation(event['accountId'],
- 'COMPLIANT',
- event)
- return build_evaluation(event['accountId'],
- 'NON_COMPLIANT',
- event, annotation='DRT team does not have access to account.')
-
-def evaluate_parameters(rule_parameters):
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return status in ("OK", "ResourceDiscovered") and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_DRT_ACCESS/SHIELD_DRT_ACCESS_test.py b/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_DRT_ACCESS/SHIELD_DRT_ACCESS_test.py
deleted file mode 100644
index 15afb90..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_DRT_ACCESS/SHIELD_DRT_ACCESS_test.py
+++ /dev/null
@@ -1,151 +0,0 @@
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-SHIELD_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 'shield':
- return SHIELD_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('SHIELD_DRT_ACCESS')
-
-class SampleTest(unittest.TestCase):
-
- def test_scenario_1(self):
- SHIELD_CLIENT_MOCK.describe_drt_access = MagicMock(side_effect=botocore.exceptions.ClientError({'Error':{'Code':'ResourceNotFoundException', 'Message': 'The subscription does not exist.'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- assert_successful_evaluation(self,
- response,
- [build_expected_response('NOT_APPLICABLE',
- '123456789012',
- annotation='AWS Shield Advanced is not enabled.')])
-
- def test_scenario_2(self):
- SHIELD_CLIENT_MOCK.describe_drt_access = MagicMock(return_value={})
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- assert_successful_evaluation(self,
- response,
- [build_expected_response('NON_COMPLIANT',
- '123456789012',
- annotation='DRT team does not have access to account.')])
-
- def test_scenario_3(self):
- SHIELD_CLIENT_MOCK.describe_drt_access = MagicMock(return_value={'RoleArn':'arn:aws:iam::123456789012:role/test'})
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- assert_successful_evaluation(self,
- response,
- [build_expected_response('COMPLIANT',
- '123456789012')])
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- if isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_DRT_ACCESS/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_DRT_ACCESS/parameters.json
deleted file mode 100644
index 12bb74e..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SHIELD_DRT_ACCESS/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "CodeKey": "SHIELD_DRT_ACCESS.zip",
- "SourceRuntime": "python3.6",
- "SourcePeriodic": "One_Hour",
- "RuleName": "SHIELD_DRT_ACCESS",
- "OptionalParameters": "{}",
- "InputParameters": "{}"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SNS_ENCRYPTED_TOPIC_CHECK/SNS_ENCRYPTED_TOPIC_CHECK.py b/vendor/github.com/awslabs/aws-config-rules/python/SNS_ENCRYPTED_TOPIC_CHECK/SNS_ENCRYPTED_TOPIC_CHECK.py
deleted file mode 100644
index 59e928e..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SNS_ENCRYPTED_TOPIC_CHECK/SNS_ENCRYPTED_TOPIC_CHECK.py
+++ /dev/null
@@ -1,450 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-'''
-Rule Name:
- SNS_ENCRYPTED_TOPIC_CHECK
-
-Description:
- Checks whether the Amazon Simple Notification Service (SNS) topics are encrypted. The rule is NON_COMPLIANT if SNS topic is not encrypted or encrypted with a different key is specfied in the "KmsKeyId" parameter.
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::SNS::Topic
-
-Rule Parameters:
- KmsKeyId
- (Optional) Comma-separated list of KMS keys ARN that should used to encrypt the Amazon Simple Notification Service
-
-Scenarios:
- Scenario: 1
- Given: KmsKeyId parameter is configured
- And: KmsKeyId does not contain valid KMS Key ARN(s)
- Then: Return ERROR
- Scenario: 2
- Given: No SNS topic is present
- Then: Return NOT_APPLICABLE
- Scenario: 3
- Given: There is at least 1 SNS topic
- And: SNS topic is not encrypted
- Then: Return NON_COMPLIANT with Annotation "The Amazon Simple Notification Service topic is not encrypted."
- Scenario: 4
- Given: There is at least 1 SNS topic
- And: SNS topic is encrypted
- And: KmsKeyId parameter is not configured
- Then: Return COMPLIANT
- Scenario: 5
- Given: There is at least 1 SNS topic
- And: SNS topic is encrypted
- And: KmsKeyId parameter is configured
- And: SNS topic encryption Key not matching any KMS key(s) in KmsKeyId
- Then: Return NON_COMPLIANT with Annotation "This SNS topic is not encrypted with KMS Key {KmsKeyId}."
- Scenario: 6
- Given: There is at least 1 SNS topic
- And: SNS topic is encrypted
- And: KmsKeyId parameter is configured
- And: SNS topic encryption Key matching one of the KMS key in KmsKeyId
- Then: Return COMPLIANT
-'''
-
-import json
-import sys
-import datetime
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::SNS::Topic'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- sns_client = get_client('sns', event)
- topic_list_of_dict = get_all_topic(sns_client)
- result = []
- if not topic_list_of_dict:
- return build_evaluation(
- event['accountId'],
- 'NOT_APPLICABLE',
- event,
- resource_type='AWS::::Account'
- )
-
- for topic_dict in topic_list_of_dict:
- response_topic_attributes_dict = sns_client.get_topic_attributes(TopicArn=topic_dict['TopicArn'])
-
- if "KmsMasterKeyId" not in response_topic_attributes_dict['Attributes']:
- result.append(build_evaluation(
- topic_dict['TopicArn'],
- 'NON_COMPLIANT',
- event,
- annotation="The Amazon Simple Notification Service topic is not encrypted."
- ))
- continue
-
- if not valid_rule_parameters:
- result.append(build_evaluation(topic_dict['TopicArn'], 'COMPLIANT', event))
- continue
-
- if response_topic_attributes_dict['Attributes']['KmsMasterKeyId'] in valid_rule_parameters:
- result.append(build_evaluation(topic_dict['TopicArn'], 'COMPLIANT', event))
- else:
- result.append(build_evaluation(
- topic_dict['TopicArn'],
- 'NON_COMPLIANT',
- event,
- annotation="This SNS topic is not encrypted with KMS Key {KmsKeyId}: "+str(valid_rule_parameters)
- ))
- return result
-
-
-# Return all the topic arn in the account
-def get_all_topic(sns_client):
- next_token = None
- result_list = []
-
- while True:
- response_list_topics_dict = sns_client.list_topics(NextToken=next_token)
- result_list.extend(response_list_topics_dict['Topics'])
- if 'NextToken' in response_list_topics_dict:
- next_token = response_list_topics_dict['NextToken']
- else:
- return result_list
-
-#Return valid list of KMS Key Ids
-def evaluate_parameters(rule_parameters):
- kmskeyid_list = {}
- if "KmsKeyId" in rule_parameters:
- kmskeyid_list = [kmskeyid.strip() for kmskeyid in rule_parameters['KmsKeyId'].split(',')]
- kmskeyid_list = list(filter(None, kmskeyid_list))
-
- for arn in kmskeyid_list:
- if not arn.startswith("arn:aws:kms:"):
- raise ValueError(
- 'Invalid value for the parameter "KmsKeyId", expected valid ARN(s) of Kms Key'
- )
- return kmskeyid_list
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return status in ('OK', 'ResourceDiscovered') and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SNS_ENCRYPTED_TOPIC_CHECK/SNS_ENCRYPTED_TOPIC_CHECK_test.py b/vendor/github.com/awslabs/aws-config-rules/python/SNS_ENCRYPTED_TOPIC_CHECK/SNS_ENCRYPTED_TOPIC_CHECK_test.py
deleted file mode 100644
index 65da6df..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SNS_ENCRYPTED_TOPIC_CHECK/SNS_ENCRYPTED_TOPIC_CHECK_test.py
+++ /dev/null
@@ -1,253 +0,0 @@
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::SNS::Topic'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-SNS_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 'sns':
- return SNS_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('SNS_ENCRYPTED_TOPIC_CHECK')
-
-class InvalidRuleParameter(unittest.TestCase):
-
- def test_scenario_1_error(self):
- list_topics_result = {"Topics": [{"TopicArn": "arn:aws:sns:ap-southeast-1:123456789012:testSNS"}]}
-
- get_topic_attributes_result = {
- "Attributes": {
- "KmsMasterKeyId": "arn:aws:kms:ap-southeast-1:123456789012:key/86a9f691-c02f-4046-9360-903afec68edc"
- }}
-
- SNS_CLIENT_MOCK.list_topics = MagicMock(return_value=list_topics_result)
- SNS_CLIENT_MOCK.get_topic_attributes = MagicMock(return_value=get_topic_attributes_result)
- rule_parameters = '{"KmsKeyId": "99a9f661-c02f-4046-9360-9334dex68gdc"}'
- lambda_result = RULE.lambda_handler(build_lambda_scheduled_event(rule_parameters), {})
- assert_customer_error_response(
- self,
- lambda_result,
- 'InvalidParameterValueException',
- 'Invalid value for the parameter "KmsKeyId", expected valid ARN(s) of Kms Key'
- )
-
-class NotApplicable(unittest.TestCase):
-
- def test_scenario_2_not_applicable(self):
- list_topics_result = {"Topics": []}
-
- get_topic_attributes_result = {}
-
- SNS_CLIENT_MOCK.list_topics = MagicMock(return_value=list_topics_result)
- SNS_CLIENT_MOCK.get_topic_attributes = MagicMock(return_value=get_topic_attributes_result)
- rule_parameters = '{"KmsKeyId": "arn:aws:kms:ap-southeast-1:123456789012:key/99a9f661-c02f-4046-9360-9334dex68gdc"}'
- lambda_result = RULE.lambda_handler(build_lambda_scheduled_event(rule_parameters), {})
- expected_response = [build_expected_response("NOT_APPLICABLE", '123456789012', 'AWS::::Account')]
- assert_successful_evaluation(self, lambda_result, expected_response, len(lambda_result))
-
-class NonCompliantResourcesTest(unittest.TestCase):
-
- def test_scenario_3_non_compliant_resources_without_key(self):
- list_topics_result = {"Topics": [{"TopicArn": "arn:aws:sns:ap-southeast-1:123456789012:dynamodbtopic"}]}
-
- get_topic_attributes_result = {"Attributes": {}}
-
- SNS_CLIENT_MOCK.list_topics = MagicMock(return_value=list_topics_result)
- SNS_CLIENT_MOCK.get_topic_attributes = MagicMock(return_value=get_topic_attributes_result)
- lambda_result = RULE.lambda_handler(build_lambda_scheduled_event({}), {})
- expected_response = [build_expected_response(
- 'NON_COMPLIANT',
- 'arn:aws:sns:ap-southeast-1:123456789012:dynamodbtopic',
- annotation="The Amazon Simple Notification Service topic is not encrypted."
- )]
- assert_successful_evaluation(self, lambda_result, expected_response, len(lambda_result))
-
- def test_scenario_5_non_compliant_resources_with_key(self):
- list_topics_result = {"Topics": [{"TopicArn": "arn:aws:sns:ap-southeast-1:123456789012:testSNS"}]}
-
- get_topic_attributes_result = {
- "Attributes": {
- "KmsMasterKeyId": "arn:aws:kms:ap-southeast-1:123456789012:key/86a9f691-c02f-4046-9360-903afec68edc"
- }}
-
- SNS_CLIENT_MOCK.list_topics = MagicMock(return_value=list_topics_result)
- SNS_CLIENT_MOCK.get_topic_attributes = MagicMock(return_value=get_topic_attributes_result)
- rule_parameters = '{"KmsKeyId": "arn:aws:kms:ap-southeast-1:123456789012:key/99a9f661-c02f-4046-9360-9334dex68gdc"}'
- lambda_result = RULE.lambda_handler(build_lambda_scheduled_event(rule_parameters), {})
- expected_response = [build_expected_response(
- 'NON_COMPLIANT',
- 'arn:aws:sns:ap-southeast-1:123456789012:testSNS',
- annotation="This SNS topic is not encrypted with KMS Key {KmsKeyId}: ['arn:aws:kms:ap-southeast-1:123456789012:key/99a9f661-c02f-4046-9360-9334dex68gdc']"
- )]
- assert_successful_evaluation(self, lambda_result, expected_response, len(lambda_result))
-
-class CompliantResourcesTest(unittest.TestCase):
-
- def test_scenario_4_compliant_resources_without_key(self):
- list_topics_result = {"Topics": [{"TopicArn":"arn:aws:sns:ap-southeast-1:123456789012:testSNS"}]}
-
- get_topic_attributes_result = {
- "Attributes": {
- "KmsMasterKeyId": "arn:aws:kms:ap-southeast-1:123456789012:key/86a9f691-c02f-4046-9360-903afec68edc"
- }}
-
- SNS_CLIENT_MOCK.list_topics = MagicMock(return_value=list_topics_result)
- SNS_CLIENT_MOCK.get_topic_attributes = MagicMock(return_value=get_topic_attributes_result)
- lambda_result = RULE.lambda_handler(build_lambda_scheduled_event({}), {})
- expected_response = [build_expected_response(
- 'COMPLIANT',
- 'arn:aws:sns:ap-southeast-1:123456789012:testSNS'
- )]
- assert_successful_evaluation(self, lambda_result, expected_response, len(lambda_result))
-
- def test_scenario_6_compliant_resources_with_key(self):
- list_topics_result = {"Topics": [{"TopicArn": "arn:aws:sns:ap-southeast-1:123456789012:testSNS"}]}
-
- get_topic_attributes_result = {
- "Attributes": {
- "KmsMasterKeyId": "arn:aws:kms:ap-southeast-1:123456789012:key/86a9f691-c02f-4046-9360-903afec68edc"
- }}
-
- SNS_CLIENT_MOCK.list_topics = MagicMock(return_value=list_topics_result)
- SNS_CLIENT_MOCK.get_topic_attributes = MagicMock(return_value=get_topic_attributes_result)
- rule_parameters = '{"KmsKeyId": "arn:aws:kms:ap-southeast-1:123456789012:key/86a9f691-c02f-4046-9360-903afec68edc"}'
- lambda_result = RULE.lambda_handler(build_lambda_scheduled_event(rule_parameters), {})
- expected_response = [build_expected_response(
- 'COMPLIANT',
- 'arn:aws:sns:ap-southeast-1:123456789012:testSNS'
- )]
- assert_successful_evaluation(self, lambda_result, expected_response, len(lambda_result))
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SNS_ENCRYPTED_TOPIC_CHECK/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/SNS_ENCRYPTED_TOPIC_CHECK/parameters.json
deleted file mode 100644
index c475e5d..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SNS_ENCRYPTED_TOPIC_CHECK/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "SNS_ENCRYPTED_TOPIC_CHECK",
- "SourceRuntime": "python3.6",
- "CodeKey": "SNS_ENCRYPTED_TOPIC_CHECK.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{\"KmsKeyId\": \"\"}",
- "SourcePeriodic": "TwentyFour_Hours"
- },
- "Tags": "[]"
-}
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SNS_TOPIC_EMAIL_SUB_IN_DOMAINS/SNS_TOPIC_EMAIL_SUB_IN_DOMAINS.py b/vendor/github.com/awslabs/aws-config-rules/python/SNS_TOPIC_EMAIL_SUB_IN_DOMAINS/SNS_TOPIC_EMAIL_SUB_IN_DOMAINS.py
deleted file mode 100644
index 96caf39..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SNS_TOPIC_EMAIL_SUB_IN_DOMAINS/SNS_TOPIC_EMAIL_SUB_IN_DOMAINS.py
+++ /dev/null
@@ -1,432 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-"""
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- SNS_TOPIC_EMAIL_SUB_IN_DOMAINS
-
-Description:
- Check whether the emails - subscribed to SNS topics - are from specified domains. The rule is NON_COMPLAINT if the email domains from a subscription with email or email-json protocols are not on the specified domains.
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::SNS::Topic
-
-Rule Parameters:
- domainNames (Required)
- Comma-separated list of authorized domainNames (without the @, i.e. example.com,example2.com).
-
-Scenarios:
-
- Scenario 1:
- Given: The Rule parameter domainNames is not provided or not valid
- Then: Return ERROR
-
- Scenario 2:
- Given: There is no SNS Topic in the AWS Account
- Then: Return NOT_APPLICABLE
-
- Scenario 3:
- Given: At least 1 SNS Topic is present in the AWS Account
- And: The parameter domainNames is configured and valid
- And: There is no email or email-json as protocol on any given SNS Topic
- Then: Return NOT_APPLICABLE on this SNS Topic
-
- Scenario 4:
- Given: At least 1 SNS Topic is present in the AWS Account
- And: The parameter domainNames is configured and valid
- And: At least one subscriber protocol is email or email-json
- And: At least one of the email address on the Endpoint is not from the specified domain
- Then: Return NON_COMPLAINT on this SNS Topic
-
- Scenario 5:
- Given: At least 1 SNS Topic is present in the AWS Account
- And: The parameter domainNames is configured and valid
- And: At least one subscriber protocol is email or email-json
- And: All email addresses on the Endpoint are from the specified domain
- Then: Return COMPLAINT on this SNS Topic
-
-"""
-import json
-import sys
-import datetime
-import re
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::SNS::Topic'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- sns_client = get_client('sns', event)
- evaluations = []
- topic = ''
- subscription_endpoints = get_all_email_subscriptions(sns_client)
- if not subscription_endpoints:
- return None
- for endpoint in subscription_endpoints:
- domain_extract = subscription_endpoints[endpoint].split('@')
- topic = endpoint + ':' + subscription_endpoints[endpoint]
- if domain_extract[1] in valid_rule_parameters['domainNames']:
- evaluations.append(build_evaluation(topic, 'COMPLIANT', event))
- continue
- evaluations.append(build_evaluation(topic, 'NON_COMPLIANT', event, DEFAULT_RESOURCE_TYPE, annotation='Endpoint domain is not in the provided input domain names.'))
- return evaluations
-
-def get_all_email_subscriptions(client):
- valid_protocols = ['email', 'email-json']
- dict_to_return = {}
- subscriptions_list = client.list_subscriptions()
- while True:
- for subscription in subscriptions_list['Subscriptions']:
- if subscription['Protocol'] in valid_protocols:
- dict_to_return[subscription['TopicArn']] = subscription['Endpoint']
- if 'NextToken' not in subscriptions_list:
- return dict_to_return
- subscriptions_list = client.list_subscriptions(NextToken=subscriptions_list['NextToken'])
-
-def evaluate_parameters(rule_parameters):
- if not rule_parameters['domainNames']:
- raise ValueError('At least one domain name is required as input parameter.')
- domain_names = rule_parameters['domainNames'].replace(" ", "")
- domain_names_list = domain_names.split(',')
- for domain in domain_names_list:
- if not re.match(r'^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$', domain):
- raise ValueError('{} not a valid domain name.'.format(domain))
- if len(domain) > 254:
- raise ValueError('Domain name is greater than 255 characters.')
- rule_parameters['domainNames'] = domain_names_list
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- print(eval_cc)
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return status in ('OK', 'ResourceDiscovered') and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SNS_TOPIC_EMAIL_SUB_IN_DOMAINS/SNS_TOPIC_EMAIL_SUB_IN_DOMAINS_test.py b/vendor/github.com/awslabs/aws-config-rules/python/SNS_TOPIC_EMAIL_SUB_IN_DOMAINS/SNS_TOPIC_EMAIL_SUB_IN_DOMAINS_test.py
deleted file mode 100644
index 70af6cc..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SNS_TOPIC_EMAIL_SUB_IN_DOMAINS/SNS_TOPIC_EMAIL_SUB_IN_DOMAINS_test.py
+++ /dev/null
@@ -1,184 +0,0 @@
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-SNS_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 'sns':
- return SNS_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('SNS_TOPIC_EMAIL_SUB_IN_DOMAINS')
-
-class SampleTest(unittest.TestCase):
-
-
- invoking_event_iam_role_sample = '{"configurationItem":{"relatedEvents":[],"relationships":[],"configuration":{},"tags":{},"configurationItemCaptureTime":"2018-07-02T03:37:52.418Z","awsAccountId":"123456789012","configurationItemStatus":"ResourceDiscovered","resourceType":"AWS::IAM::Role","resourceId":"some-resource-id","resourceName":"some-resource-name","ARN":"some-arn"},"notificationCreationTime":"2018-07-02T23:05:34.445Z","messageType":"ConfigurationItemChangeNotification"}'
-
- def setUp(self):
- pass
-
- def test_scenario1(self):
- SNS_CLIENT_MOCK.list_subscriptions = MagicMock(return_value={"Subscriptions":[]})
- rule_param = "{\"domainNames\":\"gmailcom,notyourwish.net,merachelega.org\"}"
- lambda_event = build_lambda_scheduled_event(rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException', customer_error_message='gmailcom not a valid domain name.')
-
- def test_scenario2(self):
- SNS_CLIENT_MOCK.list_subscriptions = MagicMock(return_value={"Subscriptions":[]})
- rule_param = "{\"domainNames\":\"gmail.com,notyourwish.net,merachelega.org\"}"
- lambda_event = build_lambda_scheduled_event(rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected, 1)
-
- def test_scenario3(self):
- SNS_CLIENT_MOCK.list_subscriptions = MagicMock(return_value={"Subscriptions":[]})
- rule_param = "{\"domainNames\":\"\"}"
- lambda_event = build_lambda_scheduled_event(rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException', customer_error_message='At least one domain name is required as input parameter.')
-
- def test_scenario4(self):
- SNS_CLIENT_MOCK.list_subscriptions = MagicMock(return_value={"Subscriptions":[{
- "Owner": "123456789012",
- "Endpoint": "abc@gmail.com",
- "Protocol": "email",
- "TopicArn": "arn:aws:sns:us-east-1:123456789012:vrvamshi47email",
- "SubscriptionArn": "arn:aws:sns:us-east-1:123456789012:vrvamshi47email:2c87e66f-b659-48ed-9d3b-84f65a5510cc"
- }]})
- rule_param = "{\"domainNames\":\"gmail1.com,notyourwish.net,merachelega.org\"}"
- lambda_event = build_lambda_scheduled_event(rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'arn:aws:sns:us-east-1:123456789012:vrvamshi47email:abc@gmail.com', 'AWS::SNS::Topic', annotation='Endpoint domain is not in the provided input domain names.'))
- assert_successful_evaluation(self, response, resp_expected, 1)
-
- def test_scenario5(self):
- SNS_CLIENT_MOCK.list_subscriptions = MagicMock(return_value={"Subscriptions":[{
- "Owner": "123456789012",
- "Endpoint": "abc@gmail.com",
- "Protocol": "email",
- "TopicArn": "arn:aws:sns:us-east-1:123456789012:vrvamshi47email",
- "SubscriptionArn": "arn:aws:sns:us-east-1:123456789012:vrvamshi47email:2c87e66f-b659-48ed-9d3b-84f65a5510cc"
- }]})
- rule_param = "{\"domainNames\":\"gmail.com,notyourwish.net,merachelega.org\"}"
- lambda_event = build_lambda_scheduled_event(rule_parameters=rule_param)
- response = RULE.lambda_handler(lambda_event, {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'arn:aws:sns:us-east-1:123456789012:vrvamshi47email:abc@gmail.com', 'AWS::SNS::Topic'))
- assert_successful_evaluation(self, response, resp_expected, 1)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/SNS_TOPIC_EMAIL_SUB_IN_DOMAINS/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/SNS_TOPIC_EMAIL_SUB_IN_DOMAINS/parameters.json
deleted file mode 100644
index 1170b28..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/SNS_TOPIC_EMAIL_SUB_IN_DOMAINS/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "CodeKey": "SNS_TOPIC_EMAIL_SUB_IN_DOMAINS.zip",
- "SourceRuntime": "python3.6",
- "SourcePeriodic": "One_Hour",
- "RuleName": "SNS_TOPIC_EMAIL_SUB_IN_DOMAINS",
- "OptionalParameters": "{}",
- "InputParameters": "{\"domainNames\":\"gmail.com,notyourwish.net,merachelega.org\"}"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/VPC_DEFAULT_SECURITY_GROUP_CLOSED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/VPC_DEFAULT_SECURITY_GROUP_CLOSED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/VPC_DEFAULT_SECURITY_GROUP_CLOSED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/VPC_DEFAULT_SECURITY_GROUP_CLOSED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/VPC_DEFAULT_SECURITY_GROUP_CLOSED/parameters.json
deleted file mode 100644
index 0b0e823..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/VPC_DEFAULT_SECURITY_GROUP_CLOSED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "VPC_DEFAULT_SECURITY_GROUP_CLOSED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::EC2::SecurityGroup",
- "SourceIdentifier": "VPC_DEFAULT_SECURITY_GROUP_CLOSED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/VPC_ENDPOINT_MANUAL_ACCEPTANCE/VPC_ENDPOINT_MANUAL_ACCEPTANCE.py b/vendor/github.com/awslabs/aws-config-rules/python/VPC_ENDPOINT_MANUAL_ACCEPTANCE/VPC_ENDPOINT_MANUAL_ACCEPTANCE.py
deleted file mode 100644
index 703f3a0..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/VPC_ENDPOINT_MANUAL_ACCEPTANCE/VPC_ENDPOINT_MANUAL_ACCEPTANCE.py
+++ /dev/null
@@ -1,396 +0,0 @@
-# Copyright 2017-2018 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-'''
-#####################################
-## Gherkin ##
-#####################################
-Rule Name:
- VPC_ENDPOINT_MANUAL_ACCEPTANCE
-
-Description:
- Checks whether the VPC Endpoint Service is configured for Manual Acceptance. The rule is NON_COMPLIANT if VPC Endpoint Service is configured to accept Endpoint requests automatically.
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::EC2::VPCEndpointService
-
-Rule Parameters:
- None
-
-Scenarios:
- Scenario: 1
- Given: No customer-owned VPC Endpoint Service is present.
- Then: Return NOT_APPLICABLE
-
- Scenario: 2
- Given: At least one customer-owned VPC Endpoint Service is present.
- And: "AcceptanceRequired" Key is set to False on DescribeVpcEndpoints.
- Then: Return NON_COMPLIANT
-
- Scenario: 3
- Given: At least one customer-owned VPC Endpoint Service is present.
- And: "AcceptanceRequired" Key is set to True on DescribeVpcEndpoints.
- Then: Return COMPLIANT
-'''
-
-import json
-import sys
-import datetime
-import boto3
-import botocore
-
-try:
- import liblogging
-except ImportError:
- pass
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::EC2::VPCEndpointService'
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-# Other parameters (no change needed)
-CONFIG_ROLE_TIMEOUT_SECONDS = 900
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, valid_rule_parameters):
- ec2_client = get_client('ec2', event)
- evaluations = []
- for service in get_endpoint_services(ec2_client):
- if not service['Owner'] == 'amazon':
- if service['AcceptanceRequired']:
- evaluations.append(build_evaluation(service['ServiceName'], 'COMPLIANT', event, DEFAULT_RESOURCE_TYPE))
- continue
- evaluations.append(build_evaluation(service['ServiceName'], 'NON_COMPLIANT', event, DEFAULT_RESOURCE_TYPE, annotation='The Endpoint Service has "AcceptanceRequired" set to False.'))
- return evaluations
-
-def get_endpoint_services(ec2_client):
- endpoint_services = []
- response = ec2_client.describe_vpc_endpoint_services()
- while True:
- endpoint_services.extend(response['ServiceDetails'])
- if 'NextToken' in response:
- response = ec2_client.describe_vpc_endpoint_services(NextToken=response['NextToken'])
- continue
- return endpoint_services
-
-def evaluate_parameters(rule_parameters):
- valid_rule_parameters = rule_parameters
- return valid_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internal_error_message="Parameter value is invalid",
- internal_error_details="An ValueError was raised during the validation of the Parameter value",
- customer_error_code="InvalidParameterValueException",
- customer_error_message=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configuration_item = result['configurationItems'][0]
- return convert_api_configuration(configuration_item)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configuration_item):
- for k, v in configuration_item.items():
- if isinstance(v, datetime.datetime):
- configuration_item[k] = str(v)
- configuration_item['awsAccountId'] = configuration_item['accountId']
- configuration_item['ARN'] = configuration_item['arn']
- configuration_item['configurationStateMd5Hash'] = configuration_item['configurationItemMD5Hash']
- configuration_item['configurationItemVersion'] = configuration_item['version']
- configuration_item['configuration'] = json.loads(configuration_item['configuration'])
- if 'relationships' in configuration_item:
- for i in range(len(configuration_item['relationships'])):
- configuration_item['relationships'][i]['name'] = configuration_item['relationships'][i]['relationshipName']
- return configuration_item
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invoking_event):
- check_defined(invoking_event, 'invokingEvent')
- if is_oversized_changed_notification(invoking_event['messageType']):
- configuration_item_summary = check_defined(invoking_event['configuration_item_summary'], 'configurationItemSummary')
- return get_configuration(configuration_item_summary['resourceType'], configuration_item_summary['resourceId'], configuration_item_summary['configurationItemCaptureTime'])
- if is_scheduled_notification(invoking_event['messageType']):
- return None
- return check_defined(invoking_event['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configuration_item, event):
- try:
- check_defined(configuration_item, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configuration_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return status in ('OK', 'ResourceDiscovered') and not event_left_scope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn,
- RoleSessionName="configLambdaExecution",
- DurationSeconds=CONFIG_ROLE_TIMEOUT_SECONDS)
- if 'liblogging' in sys.modules:
- liblogging.logSession(role_arn, assume_role_response)
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-def lambda_handler(event, context):
- if 'liblogging' in sys.modules:
- liblogging.logEvent(event)
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- if configuration_item:
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- else:
- evaluations.append(build_evaluation(event['accountId'], compliance_result, event, resource_type=DEFAULT_RESOURCE_TYPE))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- result_token = event['resultToken']
- test_mode = False
- if result_token == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- test_mode = True
-
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while evaluation_copy:
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=result_token, TestMode=test_mode)
- del evaluation_copy[:100]
-
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internal_error_message, internal_error_details=None):
- return build_error_response(internal_error_message, internal_error_details, 'InternalError', 'InternalError')
-
-def build_error_response(internal_error_message, internal_error_details=None, customer_error_code=None, customer_error_message=None):
- error_response = {
- 'internalErrorMessage': internal_error_message,
- 'internalErrorDetails': internal_error_details,
- 'customerErrorMessage': customer_error_message,
- 'customerErrorCode': customer_error_code
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/VPC_ENDPOINT_MANUAL_ACCEPTANCE/VPC_ENDPOINT_MANUAL_ACCEPTANCE_test.py b/vendor/github.com/awslabs/aws-config-rules/python/VPC_ENDPOINT_MANUAL_ACCEPTANCE/VPC_ENDPOINT_MANUAL_ACCEPTANCE_test.py
deleted file mode 100644
index eb8cb99..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/VPC_ENDPOINT_MANUAL_ACCEPTANCE/VPC_ENDPOINT_MANUAL_ACCEPTANCE_test.py
+++ /dev/null
@@ -1,189 +0,0 @@
-# Copyright 2017-2019 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://aws.amazon.com/apache2.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, either express or implied. See the License for
-# the specific language governing permissions and limitations under the License.
-
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock
-except ImportError:
- from mock import MagicMock
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resouce to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::EC2::VPCEndpointService'
-
-#############
-# Main Code #
-#############
-
-CONFIG_CLIENT_MOCK = MagicMock()
-STS_CLIENT_MOCK = MagicMock()
-EC2_CLIENT_MOCK = MagicMock()
-
-class Boto3Mock():
- @staticmethod
- def client(client_name, *args, **kwargs):
- if client_name == 'config':
- return CONFIG_CLIENT_MOCK
- if client_name == 'sts':
- return STS_CLIENT_MOCK
- if client_name == 'ec2':
- return EC2_CLIENT_MOCK
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-RULE = __import__('VPC_ENDPOINT_MANUAL_ACCEPTANCE')
-
-class ComplianceTest(unittest.TestCase):
-
- no_customer_endpointservices = {"ServiceDetails": [{"ServiceName": "com.amazonaws.us-east-1.transfer.server", "Owner": "amazon", "AcceptanceRequired": False}]}
- customer_es_true = {"ServiceDetails": [{"AcceptanceRequired": True, "ServiceName": "com.amazonaws.vpce.us-west-2.vpce-svc-0000000000", "Owner": "123456789012"}, {"ServiceName": "com.amazonaws.us-east-1.transfer.server", "Owner": "amazon", "AcceptanceRequired": False}]}
- customer_es_false = {"ServiceDetails": [{"AcceptanceRequired": False, "ServiceName": "com.amazonaws.vpce.us-west-2.vpce-svc-0000000000", "Owner": "123456789012"}, {"ServiceName": "com.amazonaws.us-east-1.transfer.server", "Owner": "amazon", "AcceptanceRequired": False}]}
-
- def setUp(self):
- pass
-
- def test_sample_no_cx_es(self):
- EC2_CLIENT_MOCK.describe_vpc_endpoint_services = MagicMock(return_value=self.no_customer_endpointservices)
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NOT_APPLICABLE', '123456789012', 'AWS::::Account'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_sample_cx_es_true(self):
- EC2_CLIENT_MOCK.describe_vpc_endpoint_services = MagicMock(return_value=self.customer_es_true)
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('COMPLIANT', 'com.amazonaws.vpce.us-west-2.vpce-svc-0000000000', 'AWS::EC2::VPCEndpointService'))
- assert_successful_evaluation(self, response, resp_expected)
-
- def test_sample_cx_es_false(self):
- EC2_CLIENT_MOCK.describe_vpc_endpoint_services = MagicMock(return_value=self.customer_es_false)
- RULE.ASSUME_ROLE_MODE = False
- response = RULE.lambda_handler(build_lambda_scheduled_event(), {})
- resp_expected = []
- resp_expected.append(build_expected_response('NON_COMPLIANT', 'com.amazonaws.vpce.us-west-2.vpce-svc-0000000000', 'AWS::EC2::VPCEndpointService', 'The Endpoint Service has "AcceptanceRequired" set to False.'))
- assert_successful_evaluation(self, response, resp_expected)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(test_class, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- test_class.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- test_class.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- test_class.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- test_class.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- test_class.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- test_class.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- test_class.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- test_class.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- test_class.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- test_class.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- test_class.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(test_class, response, customer_error_code=None, customer_error_message=None):
- if customer_error_code:
- test_class.assertEqual(customer_error_code, response['customerErrorCode'])
- if customer_error_message:
- test_class.assertEqual(customer_error_message, response['customerErrorMessage'])
- test_class.assertTrue(response['customerErrorCode'])
- test_class.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- test_class.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- test_class.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- STS_CLIENT_MOCK.reset_mock(return_value=True)
- STS_CLIENT_MOCK.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- RULE.ASSUME_ROLE_MODE = True
- STS_CLIENT_MOCK.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = RULE.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/VPC_ENDPOINT_MANUAL_ACCEPTANCE/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/VPC_ENDPOINT_MANUAL_ACCEPTANCE/parameters.json
deleted file mode 100644
index 961ef0d..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/VPC_ENDPOINT_MANUAL_ACCEPTANCE/parameters.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "VPC_ENDPOINT_MANUAL_ACCEPTANCE",
- "SourceRuntime": "python3.6",
- "CodeKey": "VPC_ENDPOINT_MANUAL_ACCEPTANCE.zip",
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourcePeriodic": "One_Hour"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/VPC_FLOW_LOGS_ENABLED/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/VPC_FLOW_LOGS_ENABLED/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/VPC_FLOW_LOGS_ENABLED/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/VPC_FLOW_LOGS_ENABLED/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/VPC_FLOW_LOGS_ENABLED/parameters.json
deleted file mode 100644
index f18d317..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/VPC_FLOW_LOGS_ENABLED/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "VPC_FLOW_LOGS_ENABLED",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{\"trafficType\": \"\"}",
- "SourcePeriodic": "TwentyFour_Hours",
- "SourceIdentifier": "VPC_FLOW_LOGS_ENABLED"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/VPC_FLOW_LOGS_ENABLED_CUSTOM/VPC_FLOW_LOGS_ENABLED_CUSTOM.py b/vendor/github.com/awslabs/aws-config-rules/python/VPC_FLOW_LOGS_ENABLED_CUSTOM/VPC_FLOW_LOGS_ENABLED_CUSTOM.py
deleted file mode 100644
index 8e44a09..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/VPC_FLOW_LOGS_ENABLED_CUSTOM/VPC_FLOW_LOGS_ENABLED_CUSTOM.py
+++ /dev/null
@@ -1,553 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-'''
-#####################################
-## Gherkin ##
-#####################################
-
-Rule Name:
- vpc-flow-logs-enabled
-
-Description:
- Check whether VPCs have Flow Logs enabled.
-
-Trigger:
- Periodic
-
-Reports on:
- AWS::EC2::VPC
-
-Rule Parameters:
-
-+------------------+----------------+--------------------------------------------------------------------------------------------+
-| Parameter Name | Type | Description |
-+------------------+----------------+--------------------------------------------------------------------------------------------+
-| WhiteListedVPC | Optional | This parameter can be used to white-list VPCs, the white-listed VPCs will not be evaluated |
-| | | and be returned as COMPLIANT. Separate multiple VPCs by a comma. |
-+------------------+----------------+--------------------------------------------------------------------------------------------+
-| LogGroupName | Optional | This parameter specifies the logGroupName where the flow logs must be sent to. |
-| | | |
-+------------------+----------------+--------------------------------------------------------------------------------------------+
-| TrafficType | Optional | This parameter defines the type of traffic that is being logged for a VPC. By default, |
-| | | the rule checks for 'ALL'. Possible values are ALL, ACCEPT & REJECT |
-+------------------+----------------+--------------------------------------------------------------------------------------------+
-
-Feature:
- In order to: monitor traffic for a VPC
- As: a Security Officer
- I want: to ensure that all VPCs have Flow logs associated as per requirements.
-
-Scenarios:
-
- Scenario 1:
- Given: The parameter WhiteListedVPC or TrafficType or LogGroupName is not valid
- Then: Raise Exception
-
- Scenario 2:
- Given: The parameter WhiteListedVPC is configured and valid
- And: The VPC is in the WhiteListedVPC list
- Then: Return COMPLIANT
-
- Scenario 3:
- Given: The parameter WhiteListedVPC is neither configured nor matching the VPC
- And: The VPC does not have any Flow Logs associated
- Then: Return NON_COMPLIANT
-
- Scenario 4:
- Given: The parameter WhiteListedVPC is neither configured nor matching the VPC
- And: The VPC have no Flow Logs associated with a SUCCESS deliver-log-status
- Then: Return NON_COMPLIANT
-
- Scenario 5:
- Given: The parameter WhiteListedVPC is neither configured nor matching the VPC
- And: The parameter TrafficType is not configured
- And: The parameter LogGroupName is not configured
- And: The VPC has no Flow Logs associated with TrafficType set to ALL
- Then: Return NON_COMPLIANT
-
- Scenario 6:
- Given: The parameter WhiteListedVPC is neither configured nor matching the VPC
- And: The parameter TrafficType is not configured
- And: The parameter LogGroupName is not configured
- And: The VPC has a Flow Logs associated with TrafficType set to ALL
- Then: Return COMPLIANT
-
- Scenario 7:
- Given: The parameter WhiteListedVPC is neither configured nor matching the VPC
- And: The parameter TrafficType is not configured
- And: The parameter LogGroupName is configured and valid
- And: The VPC has no Flow Logs associated with TrafficType set to ALL and the LogGroupName matches the parameter LogGroupName
- Then: Return NON_COMPLIANT
-
- Scenario 8:
- Given: The parameter WhiteListedVPC is neither configured nor matching the VPC
- And: The parameter TrafficType is not configured
- And: The parameter LogGroupName is configured and valid
- And: The VPC has a Flow Logs associated with TrafficType set to ALL and the LogGroupName matches the parameter LogGroupName
- Then: Return COMPLIANT
-
- Scenario 9:
- Given: The parameter WhiteListedVPC is neither configured nor matching the VPC
- And: The parameter TrafficType is configured and valid
- And: The VPC has no Flow Logs associated with TrafficType matching the parameter TrafficType
- Then: Return NON_COMPLIANT
-
- Scenario 10:
- Given: The parameter WhiteListedVPC is neither configured nor matching the VPC
- And: The parameter TrafficType is configured and valid
- And: The parameter LogGroupName is configured and valid
- And: The VPC has no Flow Logs associated with TrafficType matching the parameter TrafficType and the LogGroupName matches the parameter LogGroupName
- Then: Return NON_COMPLIANT
-
- Scenario 11:
- Given: The parameter WhiteListedVPC is neither configured nor matching the VPC
- And: The parameter TrafficType is configured and valid
- And: The parameter LogGroupName is configured and valid
- And: The VPC has a Flow Logs associated with TrafficType matching the parameter TrafficType and the LogGroupName matches the parameter LogGroupName
- Then: Return COMPLIANT
-'''
-
-import json
-import datetime
-import boto3
-import botocore
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::EC2::VPC'
-
-# List of the parameter allowed for the Rule
-ALLOWED_PARAMETER_NAMES = ['WhiteListedVPC', 'TrafficType', 'LogGroupName']
-
-# Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account).
-ASSUME_ROLE_MODE = False
-
-#############
-# Main Code #
-#############
-
-def evaluate_compliance(event, configuration_item, rule_parameters):
- """Form the evaluation(s) to be return to Config Rules
-
- Return either:
- None -- when no result needs to be displayed
- a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
- a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
-
- Keyword arguments:
- event -- the event variable given in the lambda handler
- configuration_item -- the configurationItem dictionary in the invokingEvent
- valid_rule_parameters -- the output of the evaluate_parameters() representing validated parameters of the Config Rule
-
- Advanced Notes:
- 1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status, the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
- 2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
- 3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a "shadow" evaluation to feedback that the evaluation took place properly
- """
-
- evaluations = []
-
- ec2_client = get_client('ec2', event)
- vpc_id_list = get_all_vpc_id(ec2_client)
- vpc_flow_log_list = get_all_flow_logs(ec2_client, vpc_id_list)
- print(vpc_flow_log_list)
-
- for vpc_id in vpc_id_list:
- if rule_parameters['WhiteListedVPC']:
- if vpc_id in rule_parameters['WhiteListedVPC']:
- evaluations.append(build_evaluation(vpc_id, 'COMPLIANT', event, annotation='This is a WhiteListed VPC.'))
- continue
-
- flow_log_exist = False
- flow_log_no_error = False
- traffic_type_matched = False
- log_group_correct = False
-
- for vpc_flow_log in vpc_flow_log_list:
- if vpc_flow_log['ResourceId'] != vpc_id:
- continue
- flow_log_exist = True
-
- if vpc_flow_log['TrafficType'] != rule_parameters['TrafficType']:
- continue
- traffic_type_matched = True
-
- if rule_parameters['LogGroupName']:
- if rule_parameters['LogGroupName'] != vpc_flow_log['LogGroupName']:
- continue
- log_group_correct = True
-
- if 'DeliverLogsErrorMessage' in vpc_flow_log:
- delivery_error_msg = vpc_flow_log['DeliverLogsErrorMessage']
- continue
- flow_log_no_error = True
-
- if not flow_log_exist:
- evaluations.append(build_evaluation(vpc_id, 'NON_COMPLIANT', event, annotation='No flow log has been configured.'))
- continue
-
- if not traffic_type_matched:
- evaluations.append(build_evaluation(vpc_id, 'NON_COMPLIANT', event, annotation='No flow log matches with the traffic type {0}.'.format(rule_parameters['TrafficType'])))
- continue
-
- if not log_group_correct:
- evaluations.append(build_evaluation(vpc_id, 'NON_COMPLIANT', event, annotation='No flow log matches with the log group name {0}.'.format(rule_parameters['LogGroupName'])))
- continue
-
- if not flow_log_no_error:
- evaluations.append(build_evaluation(vpc_id, 'NON_COMPLIANT', event, annotation='The following error occured in the flow log delivery: {0}.'.format(delivery_error_msg)))
- continue
-
- evaluations.append(build_evaluation(vpc_id, 'COMPLIANT', event))
-
- return evaluations
-
-def get_all_flow_logs(ec2_client, vpc_list):
- flow_logs = ec2_client.describe_flow_logs(Filters=[{'Name': 'resource-id', 'Values': vpc_list}], MaxResults=1000)
- all_flow_logs = []
- while True:
- all_flow_logs += flow_logs['FlowLogs']
- if "NextToken" in flow_logs:
- flow_logs = ec2_client.describe_flow_logs(Filters=[{'Name': 'resource-id', 'Values': vpc_list}], NextToken=flow_logs["NextToken"], MaxResults=1000)
- else:
- break
- return all_flow_logs
-
-def get_all_vpc_id(ec2_client):
- vpc_list = ec2_client.describe_vpcs()['Vpcs']
- vpc_id_list = []
- for vpc in vpc_list:
- vpc_id_list.append(vpc['VpcId'])
- return vpc_id_list
-
-def evaluate_parameters(rule_parameters):
- """Evaluate the rule parameters dictionary validity. Raise a ValueError for invalid parameters.
-
- Return:
- anything suitable for the evaluate_compliance()
-
- Keyword arguments:
- rule_parameters -- the Key/Value dictionary of the Config Rules parameters
- """
-
- for key in rule_parameters:
- if key not in ALLOWED_PARAMETER_NAMES:
- raise ValueError('The parameter ' + key + ' is not a valid parameter key.')
-
- validated_rule_parameters = {}
-
- validated_rule_parameters['WhiteListedVPC'] = []
- if 'WhiteListedVPC' in rule_parameters:
- whitelisted_vpcs = rule_parameters['WhiteListedVPC'].replace(' ', '').split(',')
- for vpc in whitelisted_vpcs:
- if not vpc.startswith('vpc-'):
- raise ValueError('The parameter "WhiteListedVPC" is not a valid vpc-id format.')
- validated_rule_parameters['WhiteListedVPC'] = whitelisted_vpcs
-
- validated_rule_parameters['TrafficType'] = 'ALL'
- if 'TrafficType' in rule_parameters:
- if rule_parameters['TrafficType'] not in ['ACCEPT', 'REJECT', 'ALL']:
- raise ValueError('The parameter "TrafficType" must be ALL, ACCEPT or REJECT.')
- validated_rule_parameters['TrafficType'] = rule_parameters['TrafficType']
-
- validated_rule_parameters['LogGroupName'] = None
- if 'LogGroupName' in rule_parameters:
- validated_rule_parameters['LogGroupName'] = rule_parameters['LogGroupName']
-
- return validated_rule_parameters
-
-####################
-# Helper Functions #
-####################
-
-# Build an error to be displayed in the logs when the parameter is invalid.
-def build_parameters_value_error_response(ex):
- """Return an error dictionary when the evaluate_parameters() raises a ValueError.
-
- Keyword arguments:
- ex -- Exception text
- """
- return build_error_response(internalErrorMessage="Parameter value is invalid",
- internalErrorDetails="An ValueError was raised during the validation of the Parameter value",
- customerErrorCode="InvalidParameterValueException",
- customerErrorMessage=str(ex))
-
-# This gets the client after assuming the Config service role
-# either in the same AWS account or cross-account.
-def get_client(service, event):
- """Return the service boto client. It should be used instead of directly calling the client.
-
- Keyword arguments:
- service -- the service name used for calling the boto.client()
- event -- the event variable given in the lambda handler
- """
- if not ASSUME_ROLE_MODE:
- return boto3.client(service)
- credentials = get_assume_role_credentials(event["executionRoleArn"])
- return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
- aws_secret_access_key=credentials['SecretAccessKey'],
- aws_session_token=credentials['SessionToken']
- )
-
-# This generate an evaluation for config
-def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on scheduled rules.
-
- Keyword arguments:
- resource_id -- the unique id of the resource to report
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- event -- the event variable given in the lambda handler
- resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE)
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_cc = {}
- if annotation:
- eval_cc['Annotation'] = annotation
- eval_cc['ComplianceResourceType'] = resource_type
- eval_cc['ComplianceResourceId'] = resource_id
- eval_cc['ComplianceType'] = compliance_type
- eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime'])
- return eval_cc
-
-def build_evaluation_from_config_item(configuration_item, compliance_type, annotation=None):
- """Form an evaluation as a dictionary. Usually suited to report on configuration change rules.
-
- Keyword arguments:
- configuration_item -- the configurationItem dictionary in the invokingEvent
- compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
- annotation -- an annotation to be added to the evaluation (default None)
- """
- eval_ci = {}
- if annotation:
- eval_ci['Annotation'] = annotation
- eval_ci['ComplianceResourceType'] = configuration_item['resourceType']
- eval_ci['ComplianceResourceId'] = configuration_item['resourceId']
- eval_ci['ComplianceType'] = compliance_type
- eval_ci['OrderingTimestamp'] = configuration_item['configurationItemCaptureTime']
- return eval_ci
-
-####################
-# Boilerplate Code #
-####################
-
-# Helper function used to validate input
-def check_defined(reference, reference_name):
- if not reference:
- raise Exception('Error: ', reference_name, 'is not defined')
- return reference
-
-# Check whether the message is OversizedConfigurationItemChangeNotification or not
-def is_oversized_changed_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'OversizedConfigurationItemChangeNotification'
-
-# Check whether the message is a ScheduledNotification or not.
-def is_scheduled_notification(message_type):
- check_defined(message_type, 'messageType')
- return message_type == 'ScheduledNotification'
-
-# Get configurationItem using getResourceConfigHistory API
-# in case of OversizedConfigurationItemChangeNotification
-def get_configuration(resource_type, resource_id, configuration_capture_time):
- result = AWS_CONFIG_CLIENT.get_resource_config_history(
- resourceType=resource_type,
- resourceId=resource_id,
- laterTime=configuration_capture_time,
- limit=1)
- configurationItem = result['configurationItems'][0]
- return convert_api_configuration(configurationItem)
-
-# Convert from the API model to the original invocation model
-def convert_api_configuration(configurationItem):
- for k, v in configurationItem.items():
- if isinstance(v, datetime.datetime):
- configurationItem[k] = str(v)
- configurationItem['awsAccountId'] = configurationItem['accountId']
- configurationItem['ARN'] = configurationItem['arn']
- configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash']
- configurationItem['configurationItemVersion'] = configurationItem['version']
- configurationItem['configuration'] = json.loads(configurationItem['configuration'])
- if 'relationships' in configurationItem:
- for i in range(len(configurationItem['relationships'])):
- configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName']
- return configurationItem
-
-# Based on the type of message get the configuration item
-# either from configurationItem in the invoking event
-# or using the getResourceConfigHistiry API in getConfiguration function.
-def get_configuration_item(invokingEvent):
- check_defined(invokingEvent, 'invokingEvent')
- if is_oversized_changed_notification(invokingEvent['messageType']):
- configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary')
- return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime'])
- elif is_scheduled_notification(invokingEvent['messageType']):
- return None
- return check_defined(invokingEvent['configurationItem'], 'configurationItem')
-
-# Check whether the resource has been deleted. If it has, then the evaluation is unnecessary.
-def is_applicable(configurationItem, event):
- try:
- check_defined(configurationItem, 'configurationItem')
- check_defined(event, 'event')
- except:
- return True
- status = configurationItem['configurationItemStatus']
- eventLeftScope = event['eventLeftScope']
- if status == 'ResourceDeleted':
- print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.")
- return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope
-
-def get_assume_role_credentials(role_arn):
- sts_client = boto3.client('sts')
- try:
- assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution")
- return assume_role_response['Credentials']
- except botocore.exceptions.ClientError as ex:
- # Scrub error message for any internal account info leaks
- print(str(ex))
- if 'AccessDenied' in ex.response['Error']['Code']:
- ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role."
- else:
- ex.response['Error']['Message'] = "InternalError"
- ex.response['Error']['Code'] = "InternalError"
- raise ex
-
-# This removes older evaluation (usually useful for periodic rule not reporting on AWS::::Account).
-def clean_up_old_evaluations(latest_evaluations, event):
-
- cleaned_evaluations = []
-
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100)
-
- old_eval_list = []
-
- while True:
- for old_result in old_eval['EvaluationResults']:
- old_eval_list.append(old_result)
- if 'NextToken' in old_eval:
- next_token = old_eval['NextToken']
- old_eval = AWS_CONFIG_CLIENT.get_compliance_details_by_config_rule(
- ConfigRuleName=event['configRuleName'],
- ComplianceTypes=['COMPLIANT', 'NON_COMPLIANT'],
- Limit=100,
- NextToken=next_token)
- else:
- break
-
- for old_eval in old_eval_list:
- old_resource_id = old_eval['EvaluationResultIdentifier']['EvaluationResultQualifier']['ResourceId']
- newer_founded = False
- for latest_eval in latest_evaluations:
- if old_resource_id == latest_eval['ComplianceResourceId']:
- newer_founded = True
- if not newer_founded:
- cleaned_evaluations.append(build_evaluation(old_resource_id, "NOT_APPLICABLE", event))
-
- return cleaned_evaluations + latest_evaluations
-
-# This decorates the lambda_handler in rule_code with the actual PutEvaluation call
-def lambda_handler(event, context):
-
- global AWS_CONFIG_CLIENT
-
- #print(event)
- check_defined(event, 'event')
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- try:
- valid_rule_parameters = evaluate_parameters(rule_parameters)
- except ValueError as ex:
- return build_parameters_value_error_response(ex)
-
- try:
- AWS_CONFIG_CLIENT = get_client('config', event)
- if invoking_event['messageType'] in ['ConfigurationItemChangeNotification', 'ScheduledNotification', 'OversizedConfigurationItemChangeNotification']:
- configuration_item = get_configuration_item(invoking_event)
- if is_applicable(configuration_item, event):
- compliance_result = evaluate_compliance(event, configuration_item, valid_rule_parameters)
- else:
- compliance_result = "NOT_APPLICABLE"
- else:
- return build_internal_error_response('Unexpected message type', str(invoking_event))
- except botocore.exceptions.ClientError as ex:
- if is_internal_error(ex):
- return build_internal_error_response("Unexpected error while completing API request", str(ex))
- return build_error_response("Customer error while making API request", str(ex), ex.response['Error']['Code'], ex.response['Error']['Message'])
- except ValueError as ex:
- return build_internal_error_response(str(ex), str(ex))
-
- evaluations = []
- latest_evaluations = []
-
- if not compliance_result:
- latest_evaluations.append(build_evaluation(event['accountId'], "NOT_APPLICABLE", event, resource_type='AWS::::Account'))
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, str):
- evaluations.append(build_evaluation_from_config_item(configuration_item, compliance_result))
- elif isinstance(compliance_result, list):
- for evaluation in compliance_result:
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in evaluation:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
-
- if not missing_fields:
- latest_evaluations.append(evaluation)
- evaluations = clean_up_old_evaluations(latest_evaluations, event)
- elif isinstance(compliance_result, dict):
- missing_fields = False
- for field in ('ComplianceResourceType', 'ComplianceResourceId', 'ComplianceType', 'OrderingTimestamp'):
- if field not in compliance_result:
- print("Missing " + field + " from custom evaluation.")
- missing_fields = True
- if not missing_fields:
- evaluations.append(compliance_result)
- else:
- evaluations.append(build_evaluation_from_config_item(configuration_item, 'NOT_APPLICABLE'))
-
- # Put together the request that reports the evaluation status
- resultToken = event['resultToken']
- testMode = False
- if resultToken == 'TESTMODE':
- # Used solely for RDK test to skip actual put_evaluation API call
- testMode = True
- # Invoke the Config API to report the result of the evaluation
- evaluation_copy = []
- evaluation_copy = evaluations[:]
- while(evaluation_copy):
- AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluation_copy[:100], ResultToken=resultToken, TestMode=testMode)
- del evaluation_copy[:100]
- # Used solely for RDK test to be able to test Lambda function
- return evaluations
-
-def is_internal_error(exception):
- return ((not isinstance(exception, botocore.exceptions.ClientError)) or exception.response['Error']['Code'].startswith('5')
- or 'InternalError' in exception.response['Error']['Code'] or 'ServiceError' in exception.response['Error']['Code'])
-
-def build_internal_error_response(internalErrorMessage, internalErrorDetails=None):
- return build_error_response(internalErrorMessage, internalErrorDetails, 'InternalError', 'InternalError')
-
-def build_error_response(internalErrorMessage, internalErrorDetails=None, customerErrorCode=None, customerErrorMessage=None):
- error_response = {
- 'internalErrorMessage': internalErrorMessage,
- 'internalErrorDetails': internalErrorDetails,
- 'customerErrorMessage': customerErrorMessage,
- 'customerErrorCode': customerErrorCode
- }
- print(error_response)
- return error_response
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/VPC_FLOW_LOGS_ENABLED_CUSTOM/VPC_FLOW_LOGS_ENABLED_CUSTOM_test.py b/vendor/github.com/awslabs/aws-config-rules/python/VPC_FLOW_LOGS_ENABLED_CUSTOM/VPC_FLOW_LOGS_ENABLED_CUSTOM_test.py
deleted file mode 100644
index 29524f3..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/VPC_FLOW_LOGS_ENABLED_CUSTOM/VPC_FLOW_LOGS_ENABLED_CUSTOM_test.py
+++ /dev/null
@@ -1,400 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Created with the Rule Development Kit: https://github.com/awslabs/aws-config-rdk
-# Can be used stand-alone or with the Rule Compliance Engine: https://github.com/awslabs/aws-config-engine-for-compliance-as-code
-#
-import sys
-import unittest
-try:
- from unittest.mock import MagicMock, patch, ANY
-except ImportError:
- import mock
- from mock import MagicMock, patch, ANY
-import botocore
-from botocore.exceptions import ClientError
-
-##############
-# Parameters #
-##############
-
-# Define the default resource to report to Config Rules
-DEFAULT_RESOURCE_TYPE = 'AWS::::Account'
-
-#############
-# Main Code #
-#############
-
-config_client_mock = MagicMock()
-sts_client_mock = MagicMock()
-ec2_client_mock = MagicMock()
-
-class Boto3Mock():
- def client(self, client_name, *args, **kwargs):
- if client_name == 'config':
- return config_client_mock
- elif client_name == 'sts':
- return sts_client_mock
- elif client_name == 'ec2':
- return ec2_client_mock
- else:
- raise Exception("Attempting to create an unknown client")
-
-sys.modules['boto3'] = Boto3Mock()
-
-rule = __import__('VPC_FLOW_LOGS_ENABLED_CUSTOM')
-
-class ParameterTests(unittest.TestCase):
- # Check for any invalid parameter (such as "traffictype" instead of "TrafficType")
- def test_error_invalid_parameter_name(self):
- ec2_client_mock.reset_mock(return_value=True)
-
- ruleParam = '{"traffictype": "ACCEPT"}'
- lambdaEvent = build_lambda_scheduled_event(rule_parameters=ruleParam)
-
- response = rule.lambda_handler(lambdaEvent, {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
- # Check for any invalid parameter values (such as TrafficType as "ALLOW" instead of "ACCEPT")
- def test_invalid_traffictype_parameter_value(self):
- ec2_client_mock.reset_mock(return_value=True)
-
- ruleParam = '{"TrafficType": "ALLOW"}'
- lambdaEvent = build_lambda_scheduled_event(rule_parameters=ruleParam)
-
- response = rule.lambda_handler(lambdaEvent, {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
-
- # Check for invalid parameter values of WhiteListedVPC
- def test_error_invalid_vpc_parameter_value(self):
- ec2_client_mock.reset_mock(return_value=True)
-
- ruleParam = '{"WhiteListedVPC": "vpc-asd123, vpc_1234rd", "TrafficType": "ALLOW"}'
- lambdaEvent = build_lambda_scheduled_event(rule_parameters=ruleParam)
-
- response = rule.lambda_handler(lambdaEvent, {})
- assert_customer_error_response(self, response, 'InvalidParameterValueException')
-
-
-class ComplianceTests(unittest.TestCase):
- # Check for VPC has flow logs enabled with TrafficType as ALL and no parameter provided
- def test_COMPLIANT_Flow_Log_Enabled_TrafficType_ALL(self):
- ec2_client_mock.reset_mock(return_value=True)
-
- Des_VPC = {"Vpcs": [{"VpcId": "vpc-asd123", "CidrBlock": "10.10.0.0/16"}]}
- ec2_client_mock.describe_vpcs = MagicMock(return_value = Des_VPC)
- Des_Flow = {"FlowLogs": [{"ResourceId": "vpc-asd123", "TrafficType": "ALL"}]}
- ec2_client_mock.describe_flow_logs = MagicMock(return_value = Des_Flow)
-
- lambdaEvent = build_lambda_scheduled_event(rule_parameters='{}')
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = []
- resp_expected.append({
- 'ComplianceType': 'COMPLIANT',
- 'ComplianceResourceId': 'vpc-asd123',
- 'ComplianceResourceType': 'AWS::EC2::VPC'
- })
- assert_successful_evaluation(self, response, resp_expected)
-
-
- # Check for VPC does not have flow logs enabled and no parameter provided
- def test_NONCOMPLIANT_Flow_Log_Not_Enabled(self):
- ec2_client_mock.reset_mock(return_value=True)
-
- Des_VPC = {"Vpcs": [{"VpcId": "vpc-asd123", "CidrBlock": "10.10.0.0/16"}]}
- ec2_client_mock.describe_vpcs = MagicMock(return_value = Des_VPC)
- Des_Flow = {"FlowLogs": []}
- ec2_client_mock.describe_flow_logs = MagicMock(return_value = Des_Flow)
-
- lambdaEvent = build_lambda_scheduled_event(rule_parameters='{}')
- response = rule.lambda_handler(lambdaEvent, {})
- print(response)
- resp_expected = []
- resp_expected.append({
- 'ComplianceType': 'NON_COMPLIANT',
- 'ComplianceResourceId': 'vpc-asd123',
- 'ComplianceResourceType': 'AWS::EC2::VPC',
- 'Annotation': 'No flow log has been configured.'
- })
- assert_successful_evaluation(self, response, resp_expected)
-
- # Check for VPC has flow logs enabled but TrafficType is not ALL and noparameter provided
- def test_NONCOMPLIANT_Flow_Log_Enabled_TrafficType_Not_ALL(self):
- ec2_client_mock.reset_mock(return_value=True)
-
- Des_VPC = {"Vpcs": [{"VpcId": "vpc-asd123", "CidrBlock": "10.10.0.0/16"}]}
- ec2_client_mock.describe_vpcs = MagicMock(return_value = Des_VPC)
- Des_Flow = {"FlowLogs": [{"ResourceId": "vpc-asd123", "TrafficType": "REJECT"}]}
- ec2_client_mock.describe_flow_logs = MagicMock(return_value = Des_Flow)
-
- lambdaEvent = build_lambda_scheduled_event(rule_parameters='{}')
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = []
- resp_expected.append({
- 'ComplianceType': 'NON_COMPLIANT',
- 'ComplianceResourceId': 'vpc-asd123',
- 'ComplianceResourceType': 'AWS::EC2::VPC',
- 'Annotation': 'No flow log matches with the traffic type ALL.'
- })
- assert_successful_evaluation(self, response, resp_expected)
-
- # Check for VPC is in WhiteListedVPC parameter
- def test_COMPLIANT_VPC_Whitelisted(self):
- ec2_client_mock.reset_mock(return_value=True)
-
- Des_VPC = {"Vpcs": [{"VpcId": "vpc-asd123", "CidrBlock": "10.10.0.0/16"}]}
- ec2_client_mock.describe_vpcs = MagicMock(return_value = Des_VPC)
- ruleParam = '{"WhiteListedVPC": "vpc-asd123"}'
-
- lambdaEvent = build_lambda_scheduled_event(rule_parameters=ruleParam)
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = []
- resp_expected.append({
- 'ComplianceType': 'COMPLIANT',
- 'ComplianceResourceId': 'vpc-asd123',
- 'ComplianceResourceType': 'AWS::EC2::VPC',
- 'Annotation': 'This is a WhiteListed VPC.'
- })
- assert_successful_evaluation(self, response, resp_expected)
-
- # Check for Rule parameter TrafficType ACCEPT and VPC has flow logs enabled with TrafficType as ACCEPT
- def test_COMPLIANT_Flow_Log_Enabled_TrafficType_ACCEPT_TrafficTypeParam_ACCEPT(self):
- ec2_client_mock.reset_mock(return_value=True)
-
- Des_VPC = {"Vpcs": [{"VpcId": "vpc-asd123", "CidrBlock": "10.10.0.0/16"}]}
- ec2_client_mock.describe_vpcs = MagicMock(return_value = Des_VPC)
- Des_Flow = {"FlowLogs": [{"ResourceId": "vpc-asd123", "TrafficType": "ACCEPT"}]}
- ec2_client_mock.describe_flow_logs = MagicMock(return_value = Des_Flow)
- ruleParam = '{"TrafficType": "ACCEPT"}'
-
- lambdaEvent = build_lambda_scheduled_event(rule_parameters=ruleParam)
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = []
- resp_expected.append({
- 'ComplianceType': 'COMPLIANT',
- 'ComplianceResourceId': 'vpc-asd123',
- 'ComplianceResourceType': 'AWS::EC2::VPC'
- })
- assert_successful_evaluation(self, response, resp_expected)
-
- # Check for Rule Parameter TrafficType ACCEPT and vpc Flow log enabled with traffic type as ALL
- def test_NONCOMPLIANT_Flow_Log_Enabled_TrafficType_ALL_TrafficTypeParam_ACCEPT(self):
- ec2_client_mock.reset_mock(return_value=True)
-
- Des_VPC = {"Vpcs": [{"VpcId": "vpc-asd123", "CidrBlock": "10.10.0.0/16"}]}
- ec2_client_mock.describe_vpcs = MagicMock(return_value = Des_VPC)
- Des_Flow = {"FlowLogs": [{"ResourceId": "vpc-asd123", "TrafficType": "ALL"}]}
- ec2_client_mock.describe_flow_logs = MagicMock(return_value = Des_Flow)
- ruleParam = '{"TrafficType": "ACCEPT"}'
-
- lambdaEvent = build_lambda_scheduled_event(rule_parameters=ruleParam)
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = []
- resp_expected.append({
- 'ComplianceType': 'NON_COMPLIANT',
- 'ComplianceResourceId': 'vpc-asd123',
- 'ComplianceResourceType': 'AWS::EC2::VPC',
- 'Annotation': 'No flow log matches with the traffic type ACCEPT.'
- })
- assert_successful_evaluation(self, response, resp_expected)
-
- # Check for Rule parameter TrafficType ACCEPT and VPC has flow logs enabled with TrafficType as ACCEPT
- def test_NONCOMPLIANT_Flow_Log_Enabled_TrafficType_ACCEPT_TrafficTypeParam_ACCEPT_LogGroup_Wrong(self):
- ec2_client_mock.reset_mock(return_value=True)
-
- Des_VPC = {"Vpcs": [{"VpcId": "vpc-asd123", "CidrBlock": "10.10.0.0/16"}]}
- ec2_client_mock.describe_vpcs = MagicMock(return_value = Des_VPC)
- Des_Flow = {"FlowLogs": [{"ResourceId": "vpc-asd123", "TrafficType": "ACCEPT", "LogGroupName": "some-other-log-group"}]}
- ec2_client_mock.describe_flow_logs = MagicMock(return_value = Des_Flow)
- ruleParam = '{"TrafficType": "ACCEPT", "LogGroupName": "my-log-group"}'
-
- lambdaEvent = build_lambda_scheduled_event(rule_parameters=ruleParam)
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = []
- resp_expected.append({
- 'ComplianceType': 'NON_COMPLIANT',
- 'ComplianceResourceId': 'vpc-asd123',
- 'ComplianceResourceType': 'AWS::EC2::VPC',
- 'Annotation': 'No flow log matches with the log group name my-log-group.'
- })
- assert_successful_evaluation(self, response, resp_expected)
-
- # Check for Rule parameter TrafficType ACCEPT and VPC has flow logs enabled with TrafficType as ACCEPT
- def test_NONCOMPLIANT_Flow_Log_Enabled_TrafficType_ACCEPT_TrafficTypeParam_ACCEPT_LogGroup_Good_with_Error(self):
- ec2_client_mock.reset_mock(return_value=True)
-
- Des_VPC = {"Vpcs": [{"VpcId": "vpc-asd123", "CidrBlock": "10.10.0.0/16"}]}
- ec2_client_mock.describe_vpcs = MagicMock(return_value = Des_VPC)
- Des_Flow = {"FlowLogs": [{"ResourceId": "vpc-asd123", "TrafficType": "ACCEPT", "LogGroupName": "my-log-group", "DeliverLogsErrorMessage": "Access error"}]}
- ec2_client_mock.describe_flow_logs = MagicMock(return_value = Des_Flow)
- ruleParam = '{"TrafficType": "ACCEPT", "LogGroupName": "my-log-group"}'
-
- lambdaEvent = build_lambda_scheduled_event(rule_parameters=ruleParam)
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = []
- resp_expected.append({
- 'ComplianceType': 'NON_COMPLIANT',
- 'ComplianceResourceId': 'vpc-asd123',
- 'ComplianceResourceType': 'AWS::EC2::VPC',
- 'Annotation': 'The following error occured in the flow log delivery: Access error.'
- })
- assert_successful_evaluation(self, response, resp_expected)
-
- # Check for multiple VPC with a whitelisted VPC, parameter traffic_type as ACCEPT, a VPC which has flow log enabled as ACCEPT, a VPC which FlowLog is ALL
- def test_COMPLIANT_and_NONCOMPLIANT_mutiple_VPC(self):
- ec2_client_mock.reset_mock(return_value=True)
-
- Des_VPC = {"Vpcs": [{"VpcId": "vpc-asd123"}, {"VpcId": "vpc-pqr296"}, {"VpcId": "vpc-xyz984"}, {"VpcId": "vpc-13e48c77"}, {"VpcId": "vpc-zxc234"}]}
- ec2_client_mock.describe_vpcs = MagicMock(return_value=Des_VPC)
- Des_Flow = {"FlowLogs": [
- {"ResourceId": "vpc-xyz984", "TrafficType": "ACCEPT"},
- {"ResourceId": "vpc-13e48c77", "TrafficType": "ALL"},
- {"ResourceId": "vpc-zxc234", "TrafficType": "ACCEPT", "DeliverLogsErrorMessage": "Access error"}
- ]}
- ec2_client_mock.describe_flow_logs = MagicMock(return_value=Des_Flow)
- ruleParam = '{"WhiteListedVPC": "vpc-pqr296, vpc-15045371", "TrafficType": "ACCEPT"}'
-
- lambdaEvent = build_lambda_scheduled_event(rule_parameters=ruleParam)
- response = rule.lambda_handler(lambdaEvent, {})
- resp_expected = []
- resp_expected.append({
- 'ComplianceType': 'NON_COMPLIANT',
- 'ComplianceResourceId': 'vpc-asd123',
- 'ComplianceResourceType': 'AWS::EC2::VPC',
- 'Annotation': 'No flow log has been configured.'
- })
- resp_expected.append({
- 'ComplianceType': 'COMPLIANT',
- 'ComplianceResourceId': 'vpc-pqr296',
- 'ComplianceResourceType': 'AWS::EC2::VPC',
- 'Annotation': 'This is a WhiteListed VPC.'
- })
- resp_expected.append({
- 'ComplianceType': 'COMPLIANT',
- 'ComplianceResourceId': 'vpc-xyz984',
- 'ComplianceResourceType': 'AWS::EC2::VPC'
- })
- resp_expected.append({
- 'ComplianceType': 'NON_COMPLIANT',
- 'ComplianceResourceId': 'vpc-13e48c77',
- 'ComplianceResourceType': 'AWS::EC2::VPC',
- 'Annotation': 'No flow log matches with the traffic type ACCEPT.'
- })
- resp_expected.append({
- 'ComplianceType': 'NON_COMPLIANT',
- 'ComplianceResourceId': 'vpc-zxc234',
- 'ComplianceResourceType': 'AWS::EC2::VPC',
- 'Annotation': 'The following error occured in the flow log delivery: Access error.'
- })
- print(response)
- print(resp_expected)
- assert_successful_evaluation(self, response, resp_expected, 5)
-
-####################
-# Helper Functions #
-####################
-
-def build_lambda_configurationchange_event(invoking_event, rule_parameters=None):
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_lambda_scheduled_event(rule_parameters=None):
- invoking_event = '{"messageType":"ScheduledNotification","notificationCreationTime":"2017-12-23T22:11:18.158Z"}'
- event_to_return = {
- 'configRuleName':'myrule',
- 'executionRoleArn':'roleArn',
- 'eventLeftScope': False,
- 'invokingEvent': invoking_event,
- 'accountId': '123456789012',
- 'configRuleArn': 'arn:aws:config:us-east-1:123456789012:config-rule/config-rule-8fngan',
- 'resultToken':'token'
- }
- if rule_parameters:
- event_to_return['ruleParameters'] = rule_parameters
- return event_to_return
-
-def build_expected_response(compliance_type, compliance_resource_id, compliance_resource_type=DEFAULT_RESOURCE_TYPE, annotation=None):
- if not annotation:
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type
- }
- return {
- 'ComplianceType': compliance_type,
- 'ComplianceResourceId': compliance_resource_id,
- 'ComplianceResourceType': compliance_resource_type,
- 'Annotation': annotation
- }
-
-def assert_successful_evaluation(testClass, response, resp_expected, evaluations_count=1):
- if isinstance(response, dict):
- testClass.assertEquals(resp_expected['ComplianceType'], response['ComplianceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceType'], response['ComplianceResourceType'])
- testClass.assertEquals(resp_expected['ComplianceResourceId'], response['ComplianceResourceId'])
- testClass.assertTrue(response['OrderingTimestamp'])
- if 'Annotation' in resp_expected or 'Annotation' in response:
- testClass.assertEquals(resp_expected['Annotation'], response['Annotation'])
- elif isinstance(response, list):
- testClass.assertEquals(evaluations_count, len(response))
- for i, response_expected in enumerate(resp_expected):
- testClass.assertEquals(response_expected['ComplianceResourceId'], response[i]['ComplianceResourceId'])
- testClass.assertEquals(response_expected['ComplianceType'], response[i]['ComplianceType'])
- testClass.assertEquals(response_expected['ComplianceResourceType'], response[i]['ComplianceResourceType'])
- testClass.assertTrue(response[i]['OrderingTimestamp'])
- if 'Annotation' in response_expected or 'Annotation' in response[i]:
- testClass.assertEquals(response_expected['Annotation'], response[i]['Annotation'])
-
-def assert_customer_error_response(testClass, response, customerErrorCode=None, customerErrorMessage=None):
- if customerErrorCode:
- testClass.assertEqual(customerErrorCode, response['customerErrorCode'])
- if customerErrorMessage:
- testClass.assertEqual(customerErrorMessage, response['customerErrorMessage'])
- testClass.assertTrue(response['customerErrorCode'])
- testClass.assertTrue(response['customerErrorMessage'])
- if "internalErrorMessage" in response:
- testClass.assertTrue(response['internalErrorMessage'])
- if "internalErrorDetails" in response:
- testClass.assertTrue(response['internalErrorDetails'])
-
-def sts_mock():
- assume_role_response = {
- "Credentials": {
- "AccessKeyId": "string",
- "SecretAccessKey": "string",
- "SessionToken": "string"}}
- sts_client_mock.reset_mock(return_value=True)
- sts_client_mock.assume_role = MagicMock(return_value=assume_role_response)
-
-##################
-# Common Testing #
-##################
-
-class TestStsErrors(unittest.TestCase):
-
- def test_sts_unknown_error(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'unknown-code', 'Message': 'unknown-message'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'InternalError', 'InternalError')
-
- def test_sts_access_denied(self):
- rule.ASSUME_ROLE_MODE = True
- sts_client_mock.assume_role = MagicMock(side_effect=botocore.exceptions.ClientError(
- {'Error': {'Code': 'AccessDenied', 'Message': 'access-denied'}}, 'operation'))
- response = rule.lambda_handler(build_lambda_configurationchange_event('{}'), {})
- assert_customer_error_response(
- self, response, 'AccessDenied', 'AWS Config does not have permission to assume the IAM role.')
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/VPC_FLOW_LOGS_ENABLED_CUSTOM/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/VPC_FLOW_LOGS_ENABLED_CUSTOM/parameters.json
deleted file mode 100644
index fa4d92b..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/VPC_FLOW_LOGS_ENABLED_CUSTOM/parameters.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "Parameters": {
- "RuleName": "VPC_FLOW_LOGS_ENABLED_CUSTOM",
- "SourceRuntime": "python3.6",
- "CodeKey": "VPC_FLOW_LOGS_ENABLED_CUSTOM.zip",
- "InputParameters": "{\"trafficType\":\"\",\"LogGroupName\":\"\",\"WhiteListedVPC\":\"\"}",
- "SourcePeriodic": "TwentyFour_Hours",
- "RuleSets": []
- }
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/VPC_SG_OPEN_ONLY_TO_AUTHORIZED_PORTS/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/VPC_SG_OPEN_ONLY_TO_AUTHORIZED_PORTS/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/VPC_SG_OPEN_ONLY_TO_AUTHORIZED_PORTS/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/VPC_SG_OPEN_ONLY_TO_AUTHORIZED_PORTS/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/VPC_SG_OPEN_ONLY_TO_AUTHORIZED_PORTS/parameters.json
deleted file mode 100644
index 7fd8b5b..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/VPC_SG_OPEN_ONLY_TO_AUTHORIZED_PORTS/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "VPC_SG_OPEN_ONLY_TO_AUTHORIZED_PORTS",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{\"authorizedTcpPorts\": \"\", \"authorizedUdpPorts\": \"\"}",
- "SourceEvents": "AWS::EC2::SecurityGroup",
- "SourceIdentifier": "VPC_SG_OPEN_ONLY_TO_AUTHORIZED_PORTS"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/VPC_VPN_2_TUNNELS_UP/Readme.md b/vendor/github.com/awslabs/aws-config-rules/python/VPC_VPN_2_TUNNELS_UP/Readme.md
deleted file mode 100644
index 0676b79..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/VPC_VPN_2_TUNNELS_UP/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This rule is a managed Rule. This folder will assist you on deploying the rule using the Rule Development Kit (RDK).
-
-Managed Rules - https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
-
-Rule Development Kit (RDK) - https://github.com/awslabs/aws-config-rdk
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/VPC_VPN_2_TUNNELS_UP/parameters.json b/vendor/github.com/awslabs/aws-config-rules/python/VPC_VPN_2_TUNNELS_UP/parameters.json
deleted file mode 100644
index e8bdd5e..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/VPC_VPN_2_TUNNELS_UP/parameters.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Version": "1.0",
- "Parameters": {
- "RuleName": "VPC_VPN_2_TUNNELS_UP",
- "SourceRuntime": null,
- "CodeKey": null,
- "InputParameters": "{}",
- "OptionalParameters": "{}",
- "SourceEvents": "AWS::EC2::VPNConnection",
- "SourceIdentifier": "VPC_VPN_2_TUNNELS_UP"
- },
- "Tags": "[]"
-}
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/cloudtrail_encrypted.py b/vendor/github.com/awslabs/aws-config-rules/python/cloudtrail_encrypted.py
deleted file mode 100644
index 63a11d5..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/cloudtrail_encrypted.py
+++ /dev/null
@@ -1,94 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Ensure CloudTrail is encrypted
-# Description: Checks that tracked trails are encrypted (optionally with a specific KMS Key).
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: AWS::CloudTrail::Trail
-# Required Parameters: None
-# Optional Parameter: KMSKeyARN
-# Optional Parameter value example : arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab
-
-import json
-import boto3
-
-APPLICABLE_RESOURCES = ["AWS::CloudTrail::Trail"]
-OPTIONAL_PARAMETER = "KMSKeyARN"
-
-# Verify the optional parameter, set the parameter to "None" if not existant
-def normalize_optional_parameter(rule_parameters,optional_parameter):
- if not rule_parameters:
- rule_parameters = {optional_parameter: "None"}
- print(optional_parameter+ " set to 'None'")
- else:
- if not optional_parameter in rule_parameters:
- rule_parameters = {optional_parameter: "None"}
- print(optional_parameter+ " set to 'None'")
- else:
- print(optional_parameter+ " set to rule parameter value: " + rule_parameters[optional_parameter])
- return rule_parameters
-
-# Verify compliance
-def evaluate_compliance(configuration_item, rule_parameters, optional_parameter):
- if (configuration_item["resourceType"] not in APPLICABLE_RESOURCES) or (configuration_item["configurationItemStatus"] == "ResourceDeleted"):
- return {
- "compliance_type": "NOT_APPLICABLE",
- "annotation": "NOT_APPLICABLE"
- }
-
- compliance_status = False
- print configuration_item
- kms_key_id = configuration_item["configuration"]["kmsKeyId"]
- print kms_key_id
- if kms_key_id == rule_parameters[optional_parameter] and kms_key_id != "None":
- return {
- "compliance_type": "COMPLIANT",
- "annotation": 'Encryption is enabled with the specified KMS key [' + kms_key_id + '].'
- }
- elif rule_parameters[optional_parameter] == "None" and kms_key_id != "None":
- return {
- "compliance_type": "COMPLIANT",
- "annotation": 'Encryption is enabled (no key specified in the Rule).'
- }
- elif kms_key_id != rule_parameters[optional_parameter] and kms_key_id != "None":
- return {
- "compliance_type": "NON_COMPLIANT",
- "annotation": 'Encryption is enabled with [' + kms_key_id + ']. It is not with the specified KMS key in the rule [' + rule_parameters[optional_parameter] + '].'
- }
- else:
- return {
- "compliance_type": "NON_COMPLIANT",
- "annotation": 'Encryption is disabled.'
- }
-
-# Start of the lambda function
-def lambda_handler(event, context):
- invoking_event = json.loads(event['invokingEvent'])
- configuration_item = invoking_event["configurationItem"]
-
- rule_parameters = json.loads(event["ruleParameters"])
- print rule_parameters
-
- rule_parameters = normalize_optional_parameter(rule_parameters,OPTIONAL_PARAMETER)
- print rule_parameters
-
- evaluation = evaluate_compliance(configuration_item, rule_parameters)
- config = boto3.client('config')
-
- result_token = "No token found."
- if "resultToken" in event:
- result_token = event["resultToken"]
-
- config.put_evaluations(
- Evaluations=[
- {
- "ComplianceResourceType": configuration_item["resourceType"],
- "ComplianceResourceId": configuration_item["resourceId"],
- "ComplianceType": evaluation["compliance_type"],
- "Annotation": evaluation["annotation"],
- "OrderingTimestamp": configuration_item["configurationItemCaptureTime"]
- },
- ],
- ResultToken=result_token
- )
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/cloudtrail_lfi_activated.py b/vendor/github.com/awslabs/aws-config-rules/python/cloudtrail_lfi_activated.py
deleted file mode 100644
index 5fdffac..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/cloudtrail_lfi_activated.py
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Ensure CloudTrail log file validation is enabled
-# Description: Checks that tracked trails have log file integrity activated.
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: AWS::CloudTrail::Trail
-# Required Parameters: None
-
-import json
-import boto3
-
-APPLICABLE_RESOURCES = ["AWS::CloudTrail::Trail"]
-
-def evaluate_compliance(configuration_item):
- if (configuration_item["resourceType"] not in APPLICABLE_RESOURCES) or (configuration_item["configurationItemStatus"] == "ResourceDeleted"):
- return {
- "compliance_type": "NOT_APPLICABLE",
- "annotation": "NOT_APPLICABLE"
- }
-
- lfi_status = configuration_item["configuration"]["logFileValidationEnabled"]
-
- if lfi_status:
- return {
- "compliance_type": "COMPLIANT",
- "annotation": 'Log File Validation is enabled.'
- }
- else:
- return {
- "compliance_type": "NON_COMPLIANT",
- "annotation": 'Log File Validation is disabled.'
- }
-
-def lambda_handler(event, context):
- invoking_event = json.loads(event['invokingEvent'])
- configuration_item = invoking_event["configurationItem"]
- evaluation = evaluate_compliance(configuration_item)
- config = boto3.client('config')
-
- result_token = "No token found."
- if "resultToken" in event:
- result_token = event["resultToken"]
-
- config.put_evaluations(
- Evaluations=[
- {
- "ComplianceResourceType": configuration_item["resourceType"],
- "ComplianceResourceId": configuration_item["resourceId"],
- "ComplianceType": evaluation["compliance_type"],
- "Annotation": evaluation["annotation"],
- "OrderingTimestamp": configuration_item["configurationItemCaptureTime"]
- },
- ],
- ResultToken=result_token
- )
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/config_enabled.py b/vendor/github.com/awslabs/aws-config-rules/python/config_enabled.py
deleted file mode 100644
index ce72943..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/config_enabled.py
+++ /dev/null
@@ -1,68 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Ensure Config is enabled
-# Description: Checks that Config has been activated and if it logs to a specific bucket OR a send to a specifc SNS topic.
-#
-# Trigger Type: Periodic
-# Scope of Changes: N/A
-# Required Parameters: None
-# Optional Parameter 1 name: s3BucketName
-# Optional Parameter 1 value example: config-bucket-123456789012-ap-southeast-1
-# Optional Parameter 2 name: snsTopicARN
-# Optional Parameter 2 value example: arn:aws:sns:ap-southeast-1:123456789012:config-topic
-
-
-import boto3
-import json
-from datetime import datetime
-
-client = boto3.client('config')
-
-
-def lambda_handler(event, context):
- compliance_type = 'COMPLIANT'
-
- today = datetime.today()
- rule_parameters = json.loads(event['ruleParameters'])
-
- # First check configuration recorder is created
- config_recorder_response = client.describe_configuration_recorder_status()
-
- if 'ConfigurationRecordersStatus' not in config_recorder_response or \
- len(config_recorder_response['ConfigurationRecordersStatus']) < 1:
- compliance_type = 'NON_COMPLIANT'
-
- for config_recorder in config_recorder_response['ConfigurationRecordersStatus']:
- if not config_recorder['recording']:
- compliance_type = 'NON_COMPLIANT'
-
- # Check that there are delivery channels and that they're mapping to the appropriate buckets
- delivery_channels_response = client.describe_delivery_channels()
- print(delivery_channels_response['DeliveryChannels'])
-
- if 'DeliveryChannels' not in delivery_channels_response or len(delivery_channels_response['DeliveryChannels']) < 1:
- compliance_type = 'NON_COMPLIANT'
-
- if 's3BucketName' in rule_parameters:
- for channel in delivery_channels_response['DeliveryChannels']:
- if channel['s3BucketName'] != rule_parameters['s3BucketName']:
- compliance_type = 'NON_COMPLIANT'
-
- if 'snsTopicARN' in rule_parameters:
- for channel in delivery_channels_response['DeliveryChannels']:
- if channel['snsTopicARN'] != rule_parameters['snsTopicARN']:
- compliance_type = 'NON_COMPLIANT'
-
- client.put_evaluations(
- Evaluations=[
- {
- 'ComplianceResourceType': 'AWS::::Account',
- 'ComplianceResourceId': event['accountId'],
- 'ComplianceType': compliance_type,
- 'Annotation': 'Check if Config was enabled and also routing to the appropriate s3 bucket and sns topic',
- 'OrderingTimestamp': datetime(today.year, today.month, today.day, today.hour)
- }
- ],
- ResultToken=event['resultToken']
- )
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/config_rules_exist.py b/vendor/github.com/awslabs/aws-config-rules/python/config_rules_exist.py
deleted file mode 100644
index 8f42217..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/config_rules_exist.py
+++ /dev/null
@@ -1,68 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Ensure one or several specific Config Rules exist
-# Description: Checks that specific config rules exists, including itself if configured.
-#
-# Trigger Type: Periodic
-# Scope of Changes: N/A
-# Required Parameter name: ConfigRules
-# Required Parameter value example: config-rule-name1,config-rule-name2 (split multiple rule name with a ",")
-
-
-import boto3
-import json
-
-
-def evaluate_compliance(rule_parameters):
- if 'ConfigRules' in rule_parameters:
- rulesToCheck = []
- for rules in rule_parameters["ConfigRules"].split(","):
- rulesToCheck.append(rules)
- else:
- print("No Rules defined in parameter")
- #print rulesToCheck
- fails = 0
-
- client = boto3.client('config')
- try:
- response = client.describe_config_rules(ConfigRuleNames=rulesToCheck)
- for i in response["ConfigRules"]:
- ruleActive = i["ConfigRuleState"]
- print(i)
- if ruleActive == "ACTIVE":
- pass
- else:
- fails = fails + 1
-
- except:
- fails = fails + 1
-
- if fails == 0:
- return "COMPLIANT"
- else:
- return "NON_COMPLIANT"
-
-
-
-def lambda_handler(event, context):
- account_id = event['accountId']
- invoking_event = json.loads(event["invokingEvent"])
- print (invoking_event)
- rule_parameters = json.loads(event["ruleParameters"])
- result_token = "No token found."
- if "resultToken" in event:
- result_token = event["resultToken"]
-
- config = boto3.client("config")
- config.put_evaluations(
- Evaluations=[
- {
- 'ComplianceResourceType': 'AWS::::Account',
- 'ComplianceResourceId': account_id,
- 'ComplianceType': evaluate_compliance(rule_parameters),
- 'OrderingTimestamp': invoking_event['notificationCreationTime']
- },
- ],
- ResultToken=event['resultToken']
- )
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ec2-exposed-instance.py b/vendor/github.com/awslabs/aws-config-rules/python/ec2-exposed-instance.py
deleted file mode 100644
index 126609a..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ec2-exposed-instance.py
+++ /dev/null
@@ -1,122 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Ensure that no EC2 instances allow public access to the specified ports.
-# Description: Checks that all instances block access to the specified ports.
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: EC2:Instance
-# Accepted Parameters: examplePort1, exampleRange1, examplePort2, ...
-# Example Values: 8080, 1-1024, 2375, ...
-
-
-import json
-import boto3
-
-
-APPLICABLE_RESOURCES = ["AWS::EC2::Instance"]
-
-
-def expand_range(ports):
- if "-" in ports:
- return range(int(ports.split("-")[0]), int(ports.split("-")[1])+1)
- else:
- return [int(ports)]
-
-
-def find_exposed_ports(ip_permissions):
- exposed_ports = []
- for permission in ip_permissions:
- if next((r for r in permission["IpRanges"]
- if "0.0.0.0/0" in r["CidrIp"]), None):
- exposed_ports.extend(range(permission["FromPort"],
- permission["ToPort"]+1))
- return exposed_ports
-
-
-def find_violation(ip_permissions, forbidden_ports):
- exposed_ports = find_exposed_ports(ip_permissions)
- for forbidden in forbidden_ports:
- ports = expand_range(forbidden_ports[forbidden])
- for port in ports:
- if port in exposed_ports:
- return "A forbidden port is exposed to the internet."
-
- return None
-
-
-def evaluate_compliance(configuration_item, rule_parameters):
- if configuration_item["resourceType"] not in APPLICABLE_RESOURCES:
- return {
- "compliance_type": "NOT_APPLICABLE",
- "annotation": "The rule doesn't apply to resources of type " +
- configuration_item["resourceType"] + "."
- }
-
- if configuration_item['configurationItemStatus'] == "ResourceDeleted":
- return {
- "compliance_type": "NOT_APPLICABLE",
- "annotation": "The configurationItem was deleted and therefore cannot be validated"
- }
-
- security_groups = configuration_item["configuration"].get("securityGroups")
-
- if security_groups is None:
- return {
- "compliance_type": "NON_COMPLIANT",
- "annotation": "The instance doesn't pertain to any security groups."
- }
-
- ec2 = boto3.resource("ec2")
- for security_group in security_groups:
- ip_permissions = ec2.SecurityGroup(
- security_group["groupId"]
- ).ip_permissions
-
- violation = find_violation(
- ip_permissions,
- rule_parameters
- )
-
- if violation:
- return {
- "compliance_type": "NON_COMPLIANT",
- "annotation": violation
- }
-
- return {
- "compliance_type": "COMPLIANT",
- "annotation": "This resource is compliant with the rule."
- }
-
-
-def lambda_handler(event, context):
-
- invoking_event = json.loads(event["invokingEvent"])
- configuration_item = invoking_event["configurationItem"]
- rule_parameters = json.loads(event["ruleParameters"])
-
- result_token = "No token found."
- if "resultToken" in event:
- result_token = event["resultToken"]
-
- evaluation = evaluate_compliance(configuration_item, rule_parameters)
-
- config = boto3.client("config")
- config.put_evaluations(
- Evaluations=[
- {
- "ComplianceResourceType":
- configuration_item["resourceType"],
- "ComplianceResourceId":
- configuration_item["resourceId"],
- "ComplianceType":
- evaluation["compliance_type"],
- "Annotation":
- evaluation["annotation"],
- "OrderingTimestamp":
- configuration_item["configurationItemCaptureTime"]
- },
- ],
- ResultToken=result_token
- )
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ec2_desired_instance_type.py b/vendor/github.com/awslabs/aws-config-rules/python/ec2_desired_instance_type.py
deleted file mode 100644
index c56aa36..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ec2_desired_instance_type.py
+++ /dev/null
@@ -1,58 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Ensure all EC2 Instances are of a Given Type
-# Description: Checks that all EC2 instances are of the type specified
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: EC2:Instance
-# Required Parameter: desiredInstanceType
-# Example Value: t2.small
-#
-# See https://aws.amazon.com/ec2/instance-types/ for more instance types
-
-import boto3
-import json
-
-def is_applicable(config_item, event):
- status = config_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- test = ((status in ['OK', 'ResourceDiscovered']) and
- event_left_scope == False)
- return test
-
-
-def evaluate_compliance(config_item, rule_parameters):
- if (config_item['resourceType'] != 'AWS::EC2::Instance'):
- return 'NOT_APPLICABLE'
-
- elif (config_item['configuration']['instanceType'] ==
- rule_parameters['desiredInstanceType']):
- return 'COMPLIANT'
- else:
- return 'NON_COMPLIANT'
-
-
-def lambda_handler(event, context):
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = json.loads(event['ruleParameters'])
-
- compliance_value = 'NOT_APPLICABLE'
-
- if is_applicable(invoking_event['configurationItem'], event):
- compliance_value = evaluate_compliance(
- invoking_event['configurationItem'], rule_parameters)
-
- config = boto3.client('config')
- response = config.put_evaluations(
- Evaluations=[
- {
- 'ComplianceResourceType': invoking_event['configurationItem']['resourceType'],
- 'ComplianceResourceId': invoking_event['configurationItem']['resourceId'],
- 'ComplianceType': compliance_value,
- 'OrderingTimestamp': invoking_event['configurationItem']['configurationItemCaptureTime']
- },
- ],
- ResultToken=event['resultToken'])
-
-
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ec2_desired_lifecycle_spot.py b/vendor/github.com/awslabs/aws-config-rules/python/ec2_desired_lifecycle_spot.py
deleted file mode 100644
index c0d74e9..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ec2_desired_lifecycle_spot.py
+++ /dev/null
@@ -1,55 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Description: Checks that all EC2 instances are launched as Spot Instances for maximum cost savings
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: EC2:Instance
-# Required Parameter: desiredLifecycle
-# Required Value: spot
-#
-# See https://aws.amazon.com/ec2/spot/ to learn more about EC2 Spot Instances
-
-import boto3
-import json
-
-def is_applicable(config_item, event):
- status = config_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- test = ((status in ['OK', 'ResourceDiscovered']) and
- event_left_scope == False)
- return test
-
-def evaluate_compliance(config_item, rule_parameters):
- if (config_item['resourceType'] != 'AWS::EC2::Instance'):
- return 'NOT_APPLICABLE'
-
- elif (config_item['configuration']['instanceLifecycle'] ==
- rule_parameters['desiredLifecycle']):
- return 'COMPLIANT'
- else:
- return 'NON_COMPLIANT'
-
-def lambda_handler(event, context):
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = json.loads(event['ruleParameters'])
-
- compliance_value = 'NOT_APPLICABLE'
-
- if is_applicable(invoking_event['configurationItem'], event):
- compliance_value = evaluate_compliance(
- invoking_event['configurationItem'], rule_parameters)
-
- config = boto3.client('config')
- response = config.put_evaluations(
- Evaluations=[
- {
- 'ComplianceResourceType': invoking_event['configurationItem']['resourceType'],
- 'ComplianceResourceId': invoking_event['configurationItem']['resourceId'],
- 'ComplianceType': compliance_value,
- 'OrderingTimestamp': invoking_event['configurationItem']['configurationItemCaptureTime']
- },
- ],
- ResultToken=event['resultToken'])
-
-
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ec2_launch_wizard_security_group_prohibited.py b/vendor/github.com/awslabs/aws-config-rules/python/ec2_launch_wizard_security_group_prohibited.py
deleted file mode 100644
index e16d56e..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ec2_launch_wizard_security_group_prohibited.py
+++ /dev/null
@@ -1,93 +0,0 @@
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Description: Check that security groups prefixed with "launch-wizard"
-# are not associated with network interfaces.
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: EC2:NetworkInterface
-# Accepted Parameters: None
-# Your Lambda function execution role will need to have a policy that provides
-# the appropriate permissions. Here is a policy that you can consider.
-# You should validate this for your own environment.
-#
-# {
-# "Version": "2012-10-17",
-# "Statement": [
-# {
-# "Effect": "Allow",
-# "Action": [
-# "logs:CreateLogGroup",
-# "logs:CreateLogStream",
-# "logs:PutLogEvents"
-# ],
-# "Resource": "arn:aws:logs:*:*:*"
-# },
-# {
-# "Effect": "Allow",
-# "Action": [
-# "config:PutEvaluations"
-# ],
-# "Resource": "*"
-# }
-# ]
-# }
-
-
-import boto3
-import json
-
-
-APPLICABLE_RESOURCES = ["AWS::EC2::NetworkInterface"]
-
-
-def evaluate_compliance(configuration_item):
-
- # Start as compliant
- compliance_type = 'COMPLIANT'
- annotation = "Resource is compliant."
-
- # Check resource for applicability
- if configuration_item["resourceType"] not in APPLICABLE_RESOURCES:
- compliance_type = 'NOT_APPLICABLE'
- annotation = "The rule doesn't apply to resources of type " \
- + configuration_item["resourceType"] + "."
-
- # Check if the resource has been deleted
- elif configuration_item["configurationItemStatus"] == "ResourceDeleted":
- compliance_type = 'NOT_APPLICABLE'
- annotation = "The resource " + configuration_item["resourceId"] + " has been deleted."
- # Iterate over security groups
- else:
- for sg in configuration_item['configuration']['groups']:
- if "launch-wizard" in sg['groupName']:
- compliance_type = 'NON_COMPLIANT'
- annotation = 'A launch-wizard security group is attached to ' + configuration_item['configuration']['privateIpAddress']
- break
-
- return {
- "compliance_type": compliance_type,
- "annotation": annotation
- }
-
-
-def lambda_handler(event, context):
-
- invoking_event = json.loads(event['invokingEvent'])
- configuration_item = invoking_event["configurationItem"]
- evaluation = evaluate_compliance(configuration_item)
- config = boto3.client('config')
-
- print('Compliance evaluation for %s: %s' % (configuration_item['resourceId'], evaluation["compliance_type"]))
- print('Annotation: %s' % (evaluation["annotation"]))
-
- response = config.put_evaluations(
- Evaluations=[
- {
- 'ComplianceResourceType': invoking_event['configurationItem']['resourceType'],
- 'ComplianceResourceId': invoking_event['configurationItem']['resourceId'],
- 'ComplianceType': evaluation["compliance_type"],
- "Annotation": evaluation["annotation"],
- 'OrderingTimestamp': invoking_event['configurationItem']['configurationItemCaptureTime']
- },
- ],
- ResultToken=event['resultToken'])
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ec2_no_internet_access.py b/vendor/github.com/awslabs/aws-config-rules/python/ec2_no_internet_access.py
deleted file mode 100644
index d9db699..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ec2_no_internet_access.py
+++ /dev/null
@@ -1,172 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Ensures that there is no internet connectivity
-# Description: checks the given resource on potential internet access
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: EC2:Instance, EC2:VPC, EC2:RouteTable, EC2:Subnet, EC2:NetworkInterface
-# Optional Parameter: None
-# Example Value: N/A
-#
-# Requires additional AWS Config permissions for GetResourceConfigHistory
-
-from __future__ import print_function
-
-import json
-import boto3
-
-aws_config = boto3.client('config')
-aws_ec2 = boto3.client('ec2')
-
-# this is a utility class for parsing config rules events. RaiseInternetConnectivity inherhits from it
-class ConfigRule:
- """Base class for implementing a custom config rule in AWS Lambda"""
-
- def __init__(self, configurationItem):
- self.configurationItem = configurationItem
- self.relationships = configurationItem['relationships']
-
- def evaluate_compliance(self, configurationItem=None):
- """Actual evaluation logic will be implemented here"""
- return 'NOT_APPLICABLE'
-
- def get_relationship(self, relationships, id):
- for i in relationships:
- if i['resourceId'] == id:
- return i
- return None
-
- def find_relationships_by_type(self, type, relationships=None):
- if not relationships:
- relationships = self.relationships
-
- result = []
- for i in relationships:
- if i['resourceType'] == type:
- result.append(i)
- return result
-
- def get_related_configuration_item(self, relationship):
- result = aws_config.get_resource_config_history(
- resourceType=relationship['resourceType'],
- resourceId=relationship['resourceId'],
- limit=1,
- )
- item = result['configurationItems'][0]
-
- if item.has_key('configuration'):
- item['configuration'] = json.loads(item['configuration'])
- return item
-
- def put_evaluations(self, compliance, resultToken):
- aws_config.put_evaluations(
- Evaluations=[
- {
- 'ComplianceResourceType': self.configurationItem['resourceType'],
- 'ComplianceResourceId': self.configurationItem['resourceId'],
- 'ComplianceType': compliance,
- 'OrderingTimestamp': self.configurationItem['configurationItemCaptureTime']
- },
- ],
- ResultToken=resultToken
- )
-
-
-class RaiseInternetConnectivity(ConfigRule):
- """
- Class for checking given resources for potential internet access.
-
- Supported types are: VPC, RouteTable, Subnet, Instance, NetworkInterface
-
- Implemented checks are:
-
- VPC: Check for attached IGW
- RouteTable: Check for route to an IGW
- Subnet: check if public ip address mapping is enabled, check if assigned route table has a route to an IGW
- Instance: check if instance has a public ip assigned
- NetworkInterface: check if interface has a public ip assigned
- """
- def evaluate_compliance(self, configurationItem=None):
- if not configurationItem:
- configurationItem = self.configurationItem
- relationships = self.relationships
-
- if configurationItem['configurationItemStatus'] == 'ResourceDeleted':
- return 'NOT_APPLICABLE'
-
- # check if VPC has an internet gateway attached
- if configurationItem['resourceType'] == 'AWS::EC2::VPC':
- if self.find_relationships_by_type('AWS::EC2::InternetGateway'):
- return 'NON_COMPLIANT'
- else:
- return 'COMPLIANT'
-
- # check if the route table has a rule with an internet gateway
- if configurationItem['resourceType'] == 'AWS::EC2::RouteTable':
- return self.evaluate_route_table(configurationItem)
-
- # check the subnet for potential internet accessibility
- if configurationItem['resourceType'] == "AWS::EC2::Subnet":
- # check if subnet has configured public ip assignment as default
- if configurationItem['configuration']['mapPublicIpOnLaunch']:
- return 'NON_COMPLIANT'
-
- # check if subnet has a route to an internet gateway
- try:
- route_table = self.get_related_configuration_item(self.find_relationships_by_type('AWS::EC2::RouteTable').pop())
- except:
- # no routing table associated, get main routing table of VPC
- vpc = self.get_related_configuration_item(self.find_relationships_by_type('AWS::EC2::VPC').pop())
- route_tables = self.find_relationships_by_type('AWS::EC2::RouteTable', vpc['relationships'])
- for i in route_tables:
- r = self.get_related_configuration_item(i)
- if r['configuration']['associations'][0]['main']:
- route_table = r
- break
- else:
- raise Exception('Main route table not found', vpc)
-
- # check if assigned route table has a rule with an internet gateway
- return self.evaluate_route_table(route_table)
-
- # check if the instance has a public ip assigned
- if configurationItem['resourceType'] == 'AWS::EC2::Instance':
- if configurationItem['configuration']['publicIpAddress']:
- return 'NON_COMPLIANT'
- return 'COMPLIANT'
-
- # check if network interface has a public ip associated
- if configurationItem['resourceType'] == 'AWS::EC2::NetworkInterface':
- for i in configurationItem['configuration']['privateIpAddresses']:
- if i['association']:
- return 'NON_COMPLIANT'
- return 'COMPLIANT'
-
- return 'NOT_APPLICABLE'
-
- def evaluate_route_table(self, route_table):
- for route in route_table['configuration']['routes']:
- if route['gatewayId'] and route['gatewayId'].startswith('igw-'):
- return 'NON_COMPLIANT'
- return 'COMPLIANT'
-
-
-def lambda_handler(event, context):
- try:
- invokingEvent = json.loads(event['invokingEvent'])
- configurationItem = invokingEvent['configurationItem']
- except:
- raise Exception('Could not load configuration item', event)
-
- try:
- rule = RaiseInternetConnectivity(configurationItem)
- except:
- raise Exception('Could not process configuration item', configurationItem)
-
- compliance = rule.evaluate_compliance()
-
- print('Compliance evaluation for %s: %s' % (configurationItem['resourceId'], compliance))
-
- # inform config rules about our evaluation result
- rule.put_evaluations(compliance, event['resultToken'])
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ec2_require_ebs_snapshots_for_volumes.py b/vendor/github.com/awslabs/aws-config-rules/python/ec2_require_ebs_snapshots_for_volumes.py
deleted file mode 100644
index 27054f7..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ec2_require_ebs_snapshots_for_volumes.py
+++ /dev/null
@@ -1,152 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Ensure all EC2 Volumes have a recent EC2 Snapshot
-#
-# Trigger Type: Periodic and Change Triggered
-# Scope: Volumes
-# Required Parameters: requiredSnapshotFrequencyHours
-# Example Value: 10
-
-import boto3, botocore
-import json
-import logging
-from datetime import tzinfo, datetime, timedelta
-
-logger = logging.getLogger()
-logger.setLevel(logging.INFO)
-
-# http://stackoverflow.com/questions/796008/cant-subtract-offset-naive-and-offset-aware-datetimes
-ZERO = timedelta(0)
-class UTC(tzinfo):
- def utcoffset(self, dt):
- return ZERO
- def tzname(self, dt):
- return "UTC"
- def dst(self, dt):
- return ZERO
-utc = UTC()
-
-config = boto3.client('config')
-ec2 = boto3.client('ec2')
-
-# Removes Evaluations for deleted resources, non-recorded resources, and resources that are not applicable to the rule
-def evaluate_configuration_change_compliance(invoking_event, event_left_scope):
- evaluations = []
- config_item = invoking_event['configurationItem']
- if config_item['resourceType'] != 'AWS::EC2::Volume' or event_left_scope or config_item['configurationItemStatus'] in ['ResourceDeletedNotRecorded', 'ResourceNotRecorded', 'ResourceDeleted']:
- evaluations.append(
- {
- 'ComplianceResourceType': invoking_event['configurationItem']['resourceType'],
- 'ComplianceResourceId': config_item['resourceId'],
- 'ComplianceType': 'NOT_APPLICABLE',
- 'OrderingTimestamp': datetime.now(utc)
- }
- )
-
- return evaluations
-
-# Verifies that all volumes captured by the AWSConfig service have had a snapshot taken within the last hours
-def evaluate_scheduled_compliance(invoking_event, required_snapshot_freq_hours):
- evaluations = []
- oldest_snapshot_allowed_time = datetime.now(utc) - timedelta(hours = required_snapshot_freq_hours)
-
- # List current volumes from Config
- volumes = list_config_discovered_volumes()
- for volume in volumes:
- # Skip volumes that have been created recently
- volume_state = get_latest_state(volume)
- if volume_state['resourceCreationTime'] > oldest_snapshot_allowed_time:
- continue
-
- # Retrieve the completed snapshots for each volume
- snapshots = retrieve_snapshots_for_volume(volume)
-
- compliance = 'NON_COMPLIANT'
- for snapshot in snapshots:
- # Set to COMPLIANT only if the completed snapshot was initiated within the expected frequency
- if snapshot['StartTime'] > oldest_snapshot_allowed_time:
- compliance = 'COMPLIANT'
-
- evaluations.append(
- {
- 'ComplianceResourceType': volume['resourceType'],
- 'ComplianceResourceId': volume['resourceId'],
- 'ComplianceType': compliance,
- 'OrderingTimestamp': datetime.now(utc)
- }
- )
-
- return evaluations
-
-# Retrieves the completed snapshots for the provided volume
-def retrieve_snapshots_for_volume(volume):
- snapshots = ec2.describe_snapshots(
- Filters=[
- {
- 'Name': 'volume-id',
- 'Values': [
- volume['resourceId'],
- ]
- },
- {
- 'Name': 'status',
- 'Values': [
- 'completed',
- ]
- },
- ],
- )
- return snapshots['Snapshots']
-
-# List current volumes from AWSConfig
-def list_config_discovered_volumes():
- volumes = []
- ldr_pagination_token = ''
- while True:
- discovered_volumes_response = config.list_discovered_resources(
- resourceType='AWS::EC2::Volume',
- nextToken=ldr_pagination_token
- )
- volumes.extend(discovered_volumes_response['resourceIdentifiers'])
- if 'nextToken' in discovered_volumes_response:
- ldr_pagination_token = discovered_volumes_response['nextToken']
- else:
- break
-
- return volumes
-
-# Get the most recent state of the volume from AWSConfig
-def get_latest_state(volume):
- latest_state_list = config.get_resource_config_history(
- resourceType=volume['resourceType'],
- resourceId=volume['resourceId'],
- limit=1
- )
- return latest_state_list['configurationItems'][0]
-
-def lambda_handler(event, context):
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = json.loads(event['ruleParameters'])
-
- required_snapshot_freq_hours = 0
- if 'requiredSnapshotFrequencyHours' in rule_parameters:
- required_snapshot_freq_hours = int(rule_parameters['requiredSnapshotFrequencyHours'])
- if required_snapshot_freq_hours <= 0:
- raise Exception('requiredSnapshotFrequencyHours parameter must be a greater than 0')
-
- if invoking_event['messageType'] == 'ConfigurationItemChangeNotification':
- evaluations = evaluate_configuration_change_compliance(invoking_event, event['eventLeftScope'])
- elif invoking_event['messageType'] == 'ScheduledNotification':
- evaluations = evaluate_scheduled_compliance(invoking_event, required_snapshot_freq_hours)
- else:
- raise Exception('Unexpected message type ' + str(invoking_event))
-
- # Report Evaluations to the AWSConfig service
- while (evaluations):
- response = config.put_evaluations(
- Evaluations = evaluations[:100],
- ResultToken = event['resultToken'])
- if 'FailedEvaluations' in response and response['FailedEvaluations']:
- raise Exception('Failed to report all evaluations successfully to the AWSConfig service. Failed: ' + str(response['FailedEvaluations']))
- del evaluations[:100]
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ec2_require_security_group_by_tag.py b/vendor/github.com/awslabs/aws-config-rules/python/ec2_require_security_group_by_tag.py
deleted file mode 100644
index b480613..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ec2_require_security_group_by_tag.py
+++ /dev/null
@@ -1,68 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Ensure all EC2 Instances that have a certain tag format also have a specific security group
-# Description: Checks that all EC2 instances that have a certain tag format also have a specific security group
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: EC2:Instance
-# Required Parameters: namePattern
-# Example Value: ^prod(us|eu|br)[lw]box[0-9]{3}$ (which will match 'produslbox001')
-# Required Parameters: securityGroupName
-# Example Value: MySecGroup
-#
-
-import boto3
-import json
-import re
-
-def is_applicable(config_item, event):
- status = config_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- return ((status in ['OK', 'ResourceDiscovered']) and
- (event_left_scope == False) and
- (config_item['resourceType'] == 'AWS::EC2::Instance'))
-
-def evaluate_compliance(config_item, rule_parameters):
- # Initialize evaluation to 'not applicable', i.e. rule doesn't apply
- evaluation = 'NOT_APPLICABLE'
- configuration = config_item['configuration']
- tags = configuration['tags']
- reg = re.compile(rule_parameters['namePattern'])
- # If the config item is for an EC2 instance, then iterate through the tags for that instance
- for tag in tags:
- # Check if this is the 'Name' tag, and that it matches the provided regex value
- if (tag['key'] == 'Name') and (reg.match(tag['value']) != None):
- # if so, initialize to 'non-compliant'
- evaluation = 'NON_COMPLIANT'
- secGroups = configuration['securityGroups']
- # iterate through the security groups and see if the provided secGroup name is in the list.
- # if so, set compliance to 'compliant'
- for secGroup in secGroups:
- if (secGroup['groupName'] == rule_parameters['securityGroupName']):
- evaluation = 'COMPLIANT'
- return evaluation
-
-def lambda_handler(event, context):
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = json.loads(event['ruleParameters'])
-
- compliance_value = 'NOT_APPLICABLE'
-
- if is_applicable(invoking_event['configurationItem'], event):
- compliance_value = evaluate_compliance(
- invoking_event['configurationItem'], rule_parameters)
-
- config = boto3.client('config')
- response = config.put_evaluations(
- Evaluations=[
- {
- 'ComplianceResourceType': invoking_event['configurationItem']['resourceType'],
- 'ComplianceResourceId': invoking_event['configurationItem']['resourceId'],
- 'ComplianceType': compliance_value,
- 'OrderingTimestamp': invoking_event['configurationItem']['configurationItemCaptureTime']
- },
- ],
- ResultToken=event['resultToken'])
-
-
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ec2_require_tags_with_valid_values.py b/vendor/github.com/awslabs/aws-config-rules/python/ec2_require_tags_with_valid_values.py
deleted file mode 100644
index 0aebe6a..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ec2_require_tags_with_valid_values.py
+++ /dev/null
@@ -1,103 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Ensure that resources have required tags, and that tags have valid values.
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: EC2:Instance
-# Accepted Parameters: requiredTagKey1, requiredTagValues1, requiredTagKey2, ...
-# Example Values: 'CostCenter', 'R&D,Ops', 'Environment', 'Stage,Dev,Prod', ...
-# An asterisk '*' as the value will just check that any value is set for that key
-
-
-import json
-import boto3
-
-
-# Specify desired resource types to validate
-APPLICABLE_RESOURCES = ["AWS::EC2::Instance"]
-
-
-# Iterate through required tags ensureing each required tag is present,
-# and value is one of the given valid values
-def find_violation(current_tags, required_tags):
- violation = ""
- for rtag,rvalues in required_tags.iteritems():
- tag_present = False
- for tag in current_tags:
- if tag['key'] == rtag:
- value_match = False
- tag_present = True
- rvaluesplit = rvalues.split(",")
- for rvalue in rvaluesplit:
- if tag['value'] == rvalue:
- value_match = True
- if tag['value'] != "":
- if rvalue == "*":
- value_match = True
- if value_match == False:
- violation = violation + "\n" + tag['value'] + " doesn't match any of " + required_tags[rtag] + "!"
- if not tag_present:
- violation = violation + "\n" + "Tag " + str(rtag) + " is not present."
- if violation == "":
- return None
- return violation
-
-def evaluate_compliance(configuration_item, rule_parameters):
- if configuration_item["resourceType"] not in APPLICABLE_RESOURCES:
- return {
- "compliance_type": "NOT_APPLICABLE",
- "annotation": "The rule doesn't apply to resources of type " +
- configuration_item["resourceType"] + "."
- }
-
- if configuration_item["configurationItemStatus"] == "ResourceDeleted":
- return {
- "compliance_type": "NOT_APPLICABLE",
- "annotation": "The configurationItem was deleted and therefore cannot be validated."
- }
-
- current_tags = configuration_item["configuration"].get("tags")
- violation = find_violation(current_tags, rule_parameters)
-
- if violation:
- return {
- "compliance_type": "NON_COMPLIANT",
- "annotation": violation
- }
-
- return {
- "compliance_type": "COMPLIANT",
- "annotation": "This resource is compliant with the rule."
- }
-
-def lambda_handler(event, context):
-
- invoking_event = json.loads(event["invokingEvent"])
- configuration_item = invoking_event["configurationItem"]
- rule_parameters = json.loads(event["ruleParameters"])
-
- result_token = "No token found."
- if "resultToken" in event:
- result_token = event["resultToken"]
-
- evaluation = evaluate_compliance(configuration_item, rule_parameters)
-
- config = boto3.client("config")
- config.put_evaluations(
- Evaluations=[
- {
- "ComplianceResourceType":
- configuration_item["resourceType"],
- "ComplianceResourceId":
- configuration_item["resourceId"],
- "ComplianceType":
- evaluation["compliance_type"],
- "Annotation":
- evaluation["annotation"],
- "OrderingTimestamp":
- configuration_item["configurationItemCaptureTime"]
- },
- ],
- ResultToken=result_token
- )
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ec2_security_group_ingress.py b/vendor/github.com/awslabs/aws-config-rules/python/ec2_security_group_ingress.py
deleted file mode 100644
index c21edcd..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ec2_security_group_ingress.py
+++ /dev/null
@@ -1,223 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# ec2_security_group_ingress.py
-# Trigger Type: Change Triggered
-#
-# Date: 2016-09-25
-#
-# This file contains an AWS Lambda handler which responds to AWS Config triggers in AWS EC2 security groups.
-# The Lambda function examines changes in the security group ingress permissions to see if they differ from
-# the required permissions as specificed in the REQUIRED_PERMISSIONS variable below. If so, the Lambda
-# function adds or removes ingress ports as needed. Egress rules are not checked.
-#
-# Your Lambda function execution role will need to have a policy that provides the appropriate
-# permissions. Here is a policy that you can consider. You should validate this for your own
-# environment
-#
-#{
-# "Version": "2012-10-17",
-# "Statement": [
-# {
-# "Effect": "Allow",
-# "Action": [
-# "logs:CreateLogGroup",
-# "logs:CreateLogStream",
-# "logs:PutLogEvents"
-# ],
-# "Resource": "arn:aws:logs:*:*:*"
-# },
-# {
-# "Effect": "Allow",
-# "Action": [
-# "config:PutEvaluations",
-# "ec2:DescribeSecurityGroups",
-# "ec2:AuthorizeSecurityGroupIngress",
-# "ec2:RevokeSecurityGroupIngress"
-# ],
-# "Resource": "*"
-# }
-# ]
-#}
-#
-# NOTES:
-#
-# This code is only intended for instructional purposes and should not be used for any other use.
-
-import boto3
-import botocore
-import json
-
-
-APPLICABLE_RESOURCES = ["AWS::EC2::SecurityGroup"]
-
-# Specify the required ingress permissions using the same key layout as that provided in the
-# describe_security_group API response and authorize_security_group_ingress/egress API calls.
-
-REQUIRED_PERMISSIONS = [
-{
- "IpProtocol" : "tcp",
- "FromPort" : 80,
- "ToPort" : 80,
- "UserIdGroupPairs" : [],
- "IpRanges" : [{"CidrIp" : "0.0.0.0/0"}],
- "PrefixListIds" : [],
- "Ipv6Ranges" : []
-},
-{
- "IpProtocol" : "tcp",
- "FromPort" : 443,
- "ToPort" : 443,
- "UserIdGroupPairs" : [],
- "IpRanges" : [{"CidrIp" : "0.0.0.0/0"}],
- "PrefixListIds" : [],
- "Ipv6Ranges" : []
-}]
-
-# normalize_parameters
-#
-# Normalize all rule parameters so we can handle them consistently.
-# All keys are stored in lower case. Only boolean and numeric keys are stored.
-
-def normalize_parameters(rule_parameters):
- for key, value in rule_parameters.iteritems():
- normalized_key=key.lower()
- normalized_value=value.lower()
-
- if normalized_value == "true":
- rule_parameters[normalized_key] = True
- elif normalized_value == "false":
- rule_parameters[normalized_key] = False
- elif normalized_value.isdigit():
- rule_parameters[normalized_key] = int(normalized_value)
- else:
- rule_parameters[normalized_key] = True
- return rule_parameters
-
-# evaluate_compliance
-#
-# This is the main compliance evaluation function.
-#
-# Arguments:
-#
-# configuration_item - the configuration item obtained from the AWS Config event
-# debug_enabled - debug flag
-#
-# return values:
-#
-# compliance_type -
-#
-# NOT_APPLICABLE - (1) something other than a security group is being evaluated
-# (2) the configuration item is being deleted
-# NON_COMPLIANT - the rules do not match the required rules and we couldn't
-# fix them
-# COMPLIANT - the rules match the required rules or we were able to fix
-# them
-#
-# annotation - the annotation message for AWS Config
-
-def evaluate_compliance(configuration_item, debug_enabled):
- if configuration_item["resourceType"] not in APPLICABLE_RESOURCES:
- return {
- "compliance_type" : "NOT_APPLICABLE",
- "annotation" : "The rule doesn't apply to resources of type " +
- configuration_item["resourceType"] + "."
- }
-
- if configuration_item["configurationItemStatus"] == "ResourceDeleted":
- return {
- "compliance_type": "NOT_APPLICABLE",
- "annotation": "The configurationItem was deleted and therefore cannot be validated."
- }
-
- group_id = configuration_item["configuration"]["groupId"]
- client = boto3.client("ec2");
-
- # Call describe_security_groups because the IpPermissions that are returned
- # are in a format that can be used as the basis for input to
- # authorize_security_group_ingress and revoke_security_group_ingress.
-
- try:
- response = client.describe_security_groups(GroupIds=[group_id])
- except botocore.exceptions.ClientError as e:
- return {
- "compliance_type" : "NON_COMPLIANT",
- "annotation" : "describe_security_groups failure on group " + group_id
- }
-
- if debug_enabled:
- print("security group definition: ", json.dumps(response, indent=2))
-
- ip_permissions = response["SecurityGroups"][0]["IpPermissions"]
- authorize_permissions = [item for item in REQUIRED_PERMISSIONS if item not in ip_permissions]
- revoke_permissions = [item for item in ip_permissions if item not in REQUIRED_PERMISSIONS]
-
- if authorize_permissions or revoke_permissions:
- annotation_message = "Permissions were modified."
- else:
- annotation_message = "Permissions are correct."
-
- if authorize_permissions:
- if debug_enabled:
- print("authorizing for ", group_id, ", ip_permissions ", json.dumps(authorize_permissions, indent=2))
-
- try:
- client.authorize_security_group_ingress(GroupId=group_id, IpPermissions=authorize_permissions)
- annotation_message += " " + str(len(authorize_permissions)) +" new authorization(s)."
- except botocore.exceptions.ClientError as e:
- return {
- "compliance_type" : "NON_COMPLIANT",
- "annotation" : "authorize_security_group_ingress failure on group " + group_id
- }
-
- if revoke_permissions:
- if debug_enabled:
- print("revoking for ", group_id, ", ip_permissions ", json.dumps(revoke_permissions, indent=2))
-
- try:
- client.revoke_security_group_ingress(GroupId=group_id, IpPermissions=revoke_permissions)
- annotation_message += " " + str(len(revoke_permissions)) +" new revocation(s)."
- except botocore.exceptions.ClientError as e:
- return {
- "compliance_type" : "NON_COMPLIANT",
- "annotation" : "revoke_security_group_ingress failure on group " + group_id
- }
-
- return {
- "compliance_type": "COMPLIANT",
- "annotation": annotation_message
- }
-
-# lambda_handler
-#
-# This is the main handle for the Lambda function. AWS Lambda passes the function an event and a context.
-# If "debug" is specified as a rule parameter, then debugging is enabled.
-
-def lambda_handler(event, context):
- invoking_event = json.loads(event['invokingEvent'])
- configuration_item = invoking_event["configurationItem"]
- rule_parameters = normalize_parameters(json.loads(event["ruleParameters"]))
-
- debug_enabled = False
-
- if "debug" in rule_parameters:
- debug_enabled = rule_parameters["debug"]
-
- if debug_enabled:
- print("Received event: " + json.dumps(event, indent=2))
-
- evaluation = evaluate_compliance(configuration_item, debug_enabled)
-
- config = boto3.client('config')
-
- response = config.put_evaluations(
- Evaluations=[
- {
- 'ComplianceResourceType': invoking_event['configurationItem']['resourceType'],
- 'ComplianceResourceId': invoking_event['configurationItem']['resourceId'],
- 'ComplianceType': evaluation["compliance_type"],
- "Annotation": evaluation["annotation"],
- 'OrderingTimestamp': invoking_event['configurationItem']['configurationItemCaptureTime']
- },
- ],
- ResultToken=event['resultToken'])
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ec2_security_group_port_range_all_prohibited.py b/vendor/github.com/awslabs/aws-config-rules/python/ec2_security_group_port_range_all_prohibited.py
deleted file mode 100644
index 5b95ab8..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ec2_security_group_port_range_all_prohibited.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Description: Check that security groups do not have an inbound rule
-# with port range of "All".
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: EC2:SecurityGroup
-# Accepted Parameters: None
-# Your Lambda function execution role will need to have a policy that provides
-# the appropriate permissions. Here is a policy that you can consider.
-# You should validate this for your own environment.
-#
-# {
-# "Version": "2012-10-17",
-# "Statement": [
-# {
-# "Effect": "Allow",
-# "Action": [
-# "logs:CreateLogGroup",
-# "logs:CreateLogStream",
-# "logs:PutLogEvents"
-# ],
-# "Resource": "arn:aws:logs:*:*:*"
-# },
-# {
-# "Effect": "Allow",
-# "Action": [
-# "config:PutEvaluations"
-# ],
-# "Resource": "*"
-# }
-# ]
-# }
-
-
-import boto3
-import json
-
-
-APPLICABLE_RESOURCES = ["AWS::EC2::SecurityGroup"]
-
-
-def evaluate_compliance(configuration_item):
-
- # Start as compliant
- compliance_type = 'COMPLIANT'
- annotation = "Security group is compliant."
-
- # Check if resource was deleted
- if configuration_item['configurationItemStatus'] == "ResourceDeleted":
- compliance_type = 'NOT_APPLICABLE'
- annotation = "This resource was deleted."
-
- # Check resource for applicability
- elif configuration_item["resourceType"] not in APPLICABLE_RESOURCES:
- compliance_type = 'NOT_APPLICABLE'
- annotation = "The rule doesn't apply to resources of type " \
- + configuration_item["resourceType"] + "."
-
- else:
- # Iterate over IP permissions
- for i in configuration_item['configuration']['ipPermissions']:
- # inbound rules with no "fromPort" have a value of "All"
- if "fromPort" not in i:
- compliance_type = 'NON_COMPLIANT'
- annotation = 'Security group is not compliant.'
- break
-
- return {
- "compliance_type": compliance_type,
- "annotation": annotation
- }
-
-
-def lambda_handler(event, context):
-
- invoking_event = json.loads(event['invokingEvent'])
- configuration_item = invoking_event["configurationItem"]
- evaluation = evaluate_compliance(configuration_item)
- config = boto3.client('config')
-
- print('Compliance evaluation for %s: %s' % (configuration_item['resourceId'], evaluation["compliance_type"]))
- print('Annotation: %s' % (evaluation["annotation"]))
-
- response = config.put_evaluations(
- Evaluations=[
- {
- 'ComplianceResourceType': invoking_event['configurationItem']['resourceType'],
- 'ComplianceResourceId': invoking_event['configurationItem']['resourceId'],
- 'ComplianceType': evaluation["compliance_type"],
- "Annotation": evaluation["annotation"],
- 'OrderingTimestamp': invoking_event['configurationItem']['configurationItemCaptureTime']
- },
- ],
- ResultToken=event['resultToken'])
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ec2_security_group_protocol_all_prohibited.py b/vendor/github.com/awslabs/aws-config-rules/python/ec2_security_group_protocol_all_prohibited.py
deleted file mode 100644
index 7f69783..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ec2_security_group_protocol_all_prohibited.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Description: Check that security groups do not have an inbound rule
-# with protocol of "All".
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: EC2:SecurityGroup
-# Accepted Parameters: None
-# Your Lambda function execution role will need to have a policy that provides
-# the appropriate permissions. Here is a policy that you can consider.
-# You should validate this for your own environment.
-#
-# {
-# "Version": "2012-10-17",
-# "Statement": [
-# {
-# "Effect": "Allow",
-# "Action": [
-# "logs:CreateLogGroup",
-# "logs:CreateLogStream",
-# "logs:PutLogEvents"
-# ],
-# "Resource": "arn:aws:logs:*:*:*"
-# },
-# {
-# "Effect": "Allow",
-# "Action": [
-# "config:PutEvaluations"
-# ],
-# "Resource": "*"
-# }
-# ]
-# }
-
-
-import boto3
-import json
-
-
-APPLICABLE_RESOURCES = ["AWS::EC2::SecurityGroup"]
-
-
-def evaluate_compliance(configuration_item):
-
- # Start as compliant
- compliance_type = 'COMPLIANT'
- annotation = "Security group is compliant."
-
- # Check if resource was deleted
- if configuration_item['configurationItemStatus'] == "ResourceDeleted":
- compliance_type = 'NOT_APPLICABLE'
- annotation = "This resource was deleted."
-
- # Check resource for applicability
- elif configuration_item["resourceType"] not in APPLICABLE_RESOURCES:
- compliance_type = 'NOT_APPLICABLE'
- annotation = "The rule doesn't apply to resources of type " \
- + configuration_item["resourceType"] + "."
-
- else:
- # Iterate over IP permissions
- for ip in configuration_item['configuration']['ipPermissions']:
- if ip['ipProtocol'] == "-1":
- compliance_type = 'NON_COMPLIANT'
- annotation = 'Security group is not compliant.'
- break
-
- return {
- "compliance_type": compliance_type,
- "annotation": annotation
- }
-
-
-def lambda_handler(event, context):
-
- invoking_event = json.loads(event['invokingEvent'])
- configuration_item = invoking_event["configurationItem"]
- evaluation = evaluate_compliance(configuration_item)
- config = boto3.client('config')
-
- print('Compliance evaluation for %s: %s' % (configuration_item['resourceId'], evaluation["compliance_type"]))
- print('Annotation: %s' % (evaluation["annotation"]))
-
- response = config.put_evaluations(
- Evaluations=[
- {
- 'ComplianceResourceType': invoking_event['configurationItem']['resourceType'],
- 'ComplianceResourceId': invoking_event['configurationItem']['resourceId'],
- 'ComplianceType': evaluation["compliance_type"],
- "Annotation": evaluation["annotation"],
- 'OrderingTimestamp': invoking_event['configurationItem']['configurationItemCaptureTime']
- },
- ],
- ResultToken=event['resultToken'])
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/ec2_vpc_public_subnet.py b/vendor/github.com/awslabs/aws-config-rules/python/ec2_vpc_public_subnet.py
deleted file mode 100644
index 72aed40..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/ec2_vpc_public_subnet.py
+++ /dev/null
@@ -1,105 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Description: Check that no EC2 Instances are in Public Subnet
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: EC2:Instance
-# Accepted Parameters: None
-# Your Lambda function execution role will need to have a policy that provides the appropriate
-# permissions. Here is a policy that you can consider. You should validate this for your own
-# environment
-#{
-# "Version": "2012-10-17",
-# "Statement": [
-# {
-# "Effect": "Allow",
-# "Action": [
-# "logs:CreateLogGroup",
-# "logs:CreateLogStream",
-# "logs:PutLogEvents"
-# ],
-# "Resource": "arn:aws:logs:*:*:*"
-# },
-# {
-# "Effect": "Allow",
-# "Action": [
-# "config:PutEvaluations",
-# "ec2:DescribeRouteTables"
-# ],
-# "Resource": "*"
-# }
-# ]
-#}
-#
-
-import boto3
-import botocore
-import json
-import logging
-
-log = logging.getLogger()
-log.setLevel(logging.INFO)
-
-def evaluate_compliance(configuration_item):
- subnet_id = configuration_item["configuration"]["subnetId"]
- vpc_id = configuration_item["configuration"]["vpcId"]
- client = boto3.client("ec2");
-
- response = client.describe_route_tables()
-
- # If the subnet is explicitly associated to a route table, check if there
- # is a public route. If no explicit association exists, check if the main
- # route table has a public route.
-
- private = True
- mainTableIsPublic = False
- noExplicitAssociationFound = True
- explicitAssocationIsPublic = False
-
- for i in response['RouteTables']:
- if i['VpcId'] == vpc_id:
- for j in i['Associations']:
- if j['Main'] == True:
- for k in i['Routes']:
- if k['DestinationCidrBlock'] == '0.0.0.0/0' or k['GatewayId'].startswith('igw-'):
- mainTableIsPublic = True
- else:
- if j['SubnetId'] == subnet_id:
- noExplicitAssociationFound = False
- for k in i['Routes']:
- if k['DestinationCidrBlock'] == '0.0.0.0/0' or k['GatewayId'].startswith('igw-'):
- explicitAssocationIsPublic = True
-
- if (mainTableIsPublic and noExplicitAssociationFound) or explicitAssocationIsPublic:
- private = False
-
- if private:
- return {
- "compliance_type": "COMPLIANT",
- "annotation": 'Its in private subnet'
- }
- else:
- return {
- "compliance_type" : "NON_COMPLIANT",
- "annotation" : 'Not in private subnet'
- }
-
-def lambda_handler(event, context):
- log.debug('Event %s', event)
- invoking_event = json.loads(event['invokingEvent'])
- configuration_item = invoking_event["configurationItem"]
- evaluation = evaluate_compliance(configuration_item)
- config = boto3.client('config')
-
- response = config.put_evaluations(
- Evaluations=[
- {
- 'ComplianceResourceType': invoking_event['configurationItem']['resourceType'],
- 'ComplianceResourceId': invoking_event['configurationItem']['resourceId'],
- 'ComplianceType': evaluation["compliance_type"],
- "Annotation": evaluation["annotation"],
- 'OrderingTimestamp': invoking_event['configurationItem']['configurationItemCaptureTime']
- },
- ],
- ResultToken=event['resultToken'])
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/iam_mfa_for_console_access.py b/vendor/github.com/awslabs/aws-config-rules/python/iam_mfa_for_console_access.py
deleted file mode 100644
index ebf8306..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/iam_mfa_for_console_access.py
+++ /dev/null
@@ -1,258 +0,0 @@
-#!/usr/bin/env python
-"""This Lambda function will be invoked from AWS Config and check if all given IAM users have MFA device, if they have console access."""
-# Copyright 2017 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://aws.amazon.com/apache2.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, either express or implied. See the License for the specific
-# language governing permissions and limitations under the License.
-################################################################################
-# Use Case:
-# Log detailed information about IAM users and their MFA tokens
-#
-# Possible Results:
-# Result - Annotation
-# -----------------------------------------------------
-# Compliant - The user has MFA enabled.
-# Compliant - Compliant, console login is disabled.
-# Non-Compliant - The user does not have MFA enabled.
-#
-# Lambda Function Details:
-# Runtime: python 2.7
-# Memory: 128 MB
-# Timeout: 30 seconds
-#
-# Expected Input (Only to be ran from AWS Config, this is a sample event):
-# {
-# "invokingEvent": {
-# "configurationItemDiff": null,
-# "configurationItem": {
-# "relatedEvents": [],
-# "relationships": [],
-# "configuration": {
-# "path": "/",
-# "userName": "config-test-user1",
-# "userId": "AIDAIEJCNPAE4EONMASRY",
-# "arn": "arn:aws:iam::123456789012:user/config-test-user1",
-# "createDate": "2017-03-22T18:29:26.000Z",
-# "userPolicyList": [],
-# "groupList": [],
-# "attachedManagedPolicies": [{
-# "policyName": "IAMUserChangePassword",
-# "policyArn": "arn:aws:iam::aws:policy/IAMUserChangePassword"
-# }]
-# },
-# "supplementaryConfiguration": {},
-# "tags": {},
-# "configurationItemVersion": "1.2",
-# "configurationItemCaptureTime": "2017-07-27T20:07:35.770Z",
-# "configurationStateId": 1501186055770,
-# "awsAccountId": "123456789012",
-# "configurationItemStatus": "OK",
-# "resourceType": "AWS::IAM::User",
-# "resourceId": "AIDAIEJCNPAE4EONMASRY",
-# "resourceName": "config-test-user1",
-# "ARN": "arn:aws:iam::123456789012:user/config-test-user1",
-# "awsRegion": "global",
-# "availabilityZone": "Not Applicable",
-# "configurationStateMd5Hash": "4a51983c20f27f28c58011a5547d6cd3",
-# "resourceCreationTime": "2017-03-22T18:29:26.000Z"
-# },
-# "notificationCreationTime": "2017-12-19T04:52:22.933Z",
-# "messageType": "ConfigurationItemChangeNotification",
-# "recordVersion": "1.2"
-# }
-# }
-#
-# Expected Output:
-# None, sends all compliance data directly to AWS Config
-#
-# IAM Role Policy Example:
-# {
-# "Version": "2012-10-17",
-# "Statement": [{
-# "Effect": "Allow",
-# "Action": ["s3:GetObject"],
-# "Resource": "arn:aws:s3:::*/AWSLogs/*/Config/*"
-# }, {
-# "Effect": "Allow",
-# "Action": [
-# "config:Put*",
-# "config:Get*",
-# "config:List*",
-# "config:Describe*"
-# ],
-# "Resource": "*"
-# }, {
-# "Effect": "Allow",
-# "Action": [
-# "iam:ListMFADevices",
-# "iam:GetLoginProfile"
-# ],
-# "Resource": "*"
-# }, {
-# "Effect": "Allow",
-# "Action": [
-# "logs:CreateLogStream",
-# "logs:PutLogEvents",
-# "logs:CreateLogGroup"
-# ],
-# "Resource": "*"
-# }]
-# }
-#
-# Example AWS Lambda Function Permission:
-# aws lambda add-permission \
-# --function-name <> \
-# --statement-id 1 \
-# --principal config.amazonaws.com \
-# --action lambda:InvokeFunction \
-# --source-account <>
-#
-# Example AWS Config Rule Creation CLI Command:
-# aws configservice put-config-rule --config-rule file://rule.json
-#
-# Example AWS Config Rule Creation JSON for CLI Command:
-# {
-# "ConfigRuleName": "Detailed-MFA-Check",
-# "Description": "Evaluates whether IAM users have MFA enabled if they have console access.",
-# "Scope": {
-# "ComplianceResourceTypes": [
-# "AWS::IAM::User"
-# ]
-# },
-# "Source": {
-# "Owner": "CUSTOM_LAMBDA",
-# "SourceIdentifier": "<>",
-# "SourceDetails": [{
-# "EventSource": "aws.config",
-# "MessageType": "ConfigurationItemChangeNotification"
-# }]
-# }
-# }
-################################################################################
-# Changelog
-# 1.0.1 -- 2017/12/18
-# Fixed linter warning
-# Added more verbose instructions to comments
-# 1.0.0
-# refactored from
-# https://github.com/awslabs/aws-config-rules/blob/master/python/iam-mfa.py
-# Written by AWS Professional Services Sr. Consultant Levi Romandine
-# Made compliant with major Python linters
-# flake8 (pep8 & pyflakes)
-# Disabled E501 (line length)
-# Disabled E241 (whitespace after comma)
-# OpenStack Style Guide
-# Disabled H306 (alphabetize imports)
-# pep257
-# pycodestyle
-# pylint
-# Disabled C0301 (line length)
-# Disabled C0326 (whitespace after comma)
-from __future__ import print_function
-import json
-import boto3
-from botocore.exceptions import ClientError
-
-
-print('Loading function...')
-DEBUG_MODE = False # Manually change when debugging
-try:
- IAM_CLIENT = boto3.client('iam')
- CONFIG_CLIENT = boto3.client('config')
-except Exception as error:
- print('Error creating boto3.client for S3, error text follows:\n%s' % error)
- raise Exception(error)
-
-
-def evaluate_compliance(configuration_item):
- """Check if given IAM user has a MFA device and return a string to be used for AWS config put_evaluations."""
- if configuration_item['resourceType'] not in ['AWS::IAM::User']:
- return 'NOT_APPLICABLE', 'Not applicable.'
- if DEBUG_MODE is True:
- print('Checking user %s for compliance...' % str(configuration_item['configuration']['userName']))
- try:
- mfa_response = IAM_CLIENT.list_mfa_devices(
- UserName=configuration_item['configuration']['userName']
- )
- except Exception as error:
- print('Error listing IAM devices for user, error text follows:\n%s' % error)
- raise Exception(error)
- if mfa_response['MFADevices']:
- if DEBUG_MODE is True:
- print('User %s is COMPLIANT.' % str(configuration_item['configuration']['userName']))
- return 'COMPLIANT', 'The user has MFA enabled.'
- else:
- try:
- IAM_CLIENT.get_login_profile(
- UserName=configuration_item['configuration']['userName']
- )
- except ClientError as error:
- if error.response['Error']['Code'] == 'NoSuchEntity':
- return "COMPLIANT", "Compliant, console login is disabled."
- else:
- print('Error response is:\n%s' % str(error.response))
- print('Error getting IAM user login profile details, error text follows:\n%s' % error)
- raise Exception(error)
- if DEBUG_MODE is True:
- print('User %s is NON_COMPLIANT.' % str(configuration_item['configuration']['userName']))
- return 'NON_COMPLIANT', 'The user does not have MFA enabled.'
-
-
-def validate_invoking_event(event):
- """Verify the invoking event has all the necessary data fields."""
- if 'invokingEvent' in event:
- invoking_event = json.loads(event['invokingEvent'])
- else:
- raise Exception('Error, invokingEvent not found in event, aborting.')
- if 'resultToken' not in event:
- raise Exception('Error, resultToken not found in event, aborting.')
- if 'configurationItem' not in invoking_event:
- raise Exception("Error, configurationItem not found in event['invokingEvent'], aborting.")
- if 'resourceType' not in invoking_event['configurationItem']:
- raise Exception("Error, resourceType not found in event['invokingEvent']['configurationItem'], aborting.")
- if 'configuration' not in invoking_event['configurationItem']:
- raise Exception("Error, configuration not found in event['invokingEvent']['configurationItem'], aborting.")
- if 'userName' not in invoking_event['configurationItem']['configuration']:
- raise Exception("Error, userName not found in event['invokingEvent']['configurationItem']['configuration'], aborting.")
- if 'resourceId' not in invoking_event['configurationItem']:
- raise Exception("Error, resourceId not found in event['invokingEvent']['configurationItem'], aborting.")
- if 'configurationItemCaptureTime' not in invoking_event['configurationItem']:
- raise Exception("Error, configurationItemCaptureTime not found in event['invokingEvent']['configurationItem'], aborting.")
- return invoking_event
-
-
-def lambda_handler(event, context): # pylint: disable=W0613
- """Main Lambda function."""
- if DEBUG_MODE is True:
- print("Received event: \n%s" % json.dumps(event, indent=2))
- invoking_event = validate_invoking_event(event)
- try:
- compliance, annotation = evaluate_compliance(invoking_event['configurationItem'])
- CONFIG_CLIENT.put_evaluations(
- Evaluations=[
- {
- 'ComplianceResourceType':
- invoking_event['configurationItem']['resourceType'],
- 'ComplianceResourceId':
- invoking_event['configurationItem']['resourceId'],
- 'ComplianceType':
- compliance,
- 'Annotation':
- annotation,
- 'OrderingTimestamp':
- invoking_event['configurationItem']['configurationItemCaptureTime']
- },
- ],
- ResultToken=event['resultToken']
- )
- except Exception as error:
- print('Error submitting put_evaluation to AWS config, error text follows:\n%s' % error)
- raise Exception(error)
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/iam_policy_exists.py b/vendor/github.com/awslabs/aws-config-rules/python/iam_policy_exists.py
deleted file mode 100644
index c055ab4..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/iam_policy_exists.py
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Ensure one or several specific IAM policies exist
-# Description: Checks that defined IAM policies have been defined in AWS IAM.
-#
-# Trigger Type: Periodic
-# Scope of Changes: N/A
-# Required Parameter name: PoliciesToCheck
-# Required Parameter value example: policy-name1,policy-name2 (split multiple rule name with a ",")
-
-import boto3
-import json
-
-
-def evaluate_compliance(rule_parameters, account_id):
- fails = 0
- client = boto3.client("iam")
-
- if 'PoliciesToCheck' in rule_parameters:
- for policy in rule_parameters["PoliciesToCheck"].split(","):
- policyARN = "arn:aws:iam::%s:policy/%s" %(account_id, policy)
- print(policyARN)
- try:
- response = client.get_policy(PolicyArn=policyARN)
- except:
- fails = fails + 1
- else:
- print("No IAM policy defined in parameter")
- fails = fails + 1
- if fails == 0:
- return "COMPLIANT"
- else:
- return "NON_COMPLIANT"
-
-
-def lambda_handler(event, context):
- account_id = event['accountId']
- invoking_event = json.loads(event["invokingEvent"])
- print(invoking_event)
- rule_parameters = json.loads(event["ruleParameters"])
- result_token = "No token found."
- if "resultToken" in event:
- result_token = event["resultToken"]
-
- config = boto3.client("config")
- config.put_evaluations(
- Evaluations=[
- {
- 'ComplianceResourceType': 'AWS::::Account',
- 'ComplianceResourceId': account_id,
- 'ComplianceType': evaluate_compliance(rule_parameters, account_id),
- 'OrderingTimestamp': invoking_event['notificationCreationTime']
- },
- ],
- ResultToken=event['resultToken']
- )
\ No newline at end of file
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/iam_unused_keys.py b/vendor/github.com/awslabs/aws-config-rules/python/iam_unused_keys.py
deleted file mode 100644
index 032edb8..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/iam_unused_keys.py
+++ /dev/null
@@ -1,90 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Ensure that no users have access keys that have never been used.
-# Description: Checks that all users have only active access keys.
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: IAM:User
-
-
-import json
-import logging
-
-import boto3
-
-APPLICABLE_RESOURCES = ["AWS::IAM::User"]
-
-
-def evaluate_compliance(configuration_item):
- compliant = "COMPLIANT"
- annotations = []
-
- if configuration_item["resourceType"] not in APPLICABLE_RESOURCES:
- compliant = "NOT_APPLICABLE"
- annotations.append(
- "Cannot use this rule for resource of type {}.".format(
- configuration_item["resourceType"]))
-
- return compliant, " ".join(annotations)
-
- user_name = configuration_item["configuration"]["userName"]
-
- iam = boto3.client("iam")
- access_keys = iam.list_access_keys(UserName=user_name)["AccessKeyMetadata"]
-
- if access_keys:
- for access_key in access_keys:
- access_key_id = access_key["AccessKeyId"]
- access_key_status = access_key["Status"]
-
- last_used_date = iam.get_access_key_last_used(
- AccessKeyId=access_key_id
- ).get("AccessKeyLastUsed").get("LastUsedDate")
-
- if access_key_status == "Active" and last_used_date is None:
- compliant = "NON_COMPLIANT"
- annotations.append(
- "Access key with ID {} was never used.".format(
- access_key_id))
- else:
- annotations.append(
- "Access key with ID {} key was last used {}.".format(
- access_key_id, last_used_date))
- else:
- annotations.append("User do not have any active access key.")
-
- return compliant, " ".join(annotations)
-
-
-def lambda_handler(event, context):
- logging.debug("Input event: %s", event)
-
- invoking_event = json.loads(event["invokingEvent"])
- configuration_item = invoking_event["configurationItem"]
-
- result_token = "No token found."
- if "resultToken" in event:
- result_token = event["resultToken"]
-
- try:
- compliant, annotation = evaluate_compliance(configuration_item)
-
- config = boto3.client("config")
- config.put_evaluations(
- Evaluations=[
- {
- "ComplianceResourceType":
- configuration_item["resourceType"],
- "ComplianceResourceId":
- configuration_item["resourceId"],
- "ComplianceType": compliant,
- "Annotation": annotation,
- "OrderingTimestamp":
- configuration_item["configurationItemCaptureTime"]
- },
- ],
- ResultToken=result_token,
- )
- except Exception as exception:
- logging.error("Error computing compliance status: %s", exception)
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/lambda_require_tags_with_valid_values.py b/vendor/github.com/awslabs/aws-config-rules/python/lambda_require_tags_with_valid_values.py
deleted file mode 100644
index 2d3a958..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/lambda_require_tags_with_valid_values.py
+++ /dev/null
@@ -1,112 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Ensure that resources have required tags, and that tags have valid values.
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: AWS::Lambda::Function
-# Accepted Parameters: requiredTagKey1, requiredTagValues1, requiredTagKey2, ...
-# Example Values: 'CostCenter', 'R&D,Ops', 'Environment', 'Stage,Dev,Prod', ...
-# An asterisk '*' as the value will just check that any value is set for that key
-
-
-import json
-import boto3
-
-
-# Specify desired resource types to validate
-APPLICABLE_RESOURCES = ["AWS::Lambda::Function"]
-
-
-# Iterate through required tags ensureing each required tag is present,
-# and value is one of the given valid values
-def find_violation(current_tags, required_tags):
- violation = ""
- for rtag,rvalues in required_tags.items():
- tag_present = False
- for tag in current_tags:
- if tag == rtag:
- tag_present = True
- value_match = False
- rvaluesplit = rvalues.split(",")
- for rvalue in rvaluesplit:
- if current_tags[tag] == rvalue:
- value_match = True
- if current_tags[tag] != "":
- if rvalue == "*":
- value_match = True
- if value_match == False:
- violation = violation + "\n" + current_tags[tag] + " doesn't match any of " + required_tags[rtag] + "!"
- if not tag_present:
- violation = violation + "\n" + "Tag " + str(rtag) + " is not present."
- if violation == "":
- return None
- return violation
-
-
-def evaluate_compliance(configuration_item, rule_parameters):
-
- if configuration_item["resourceType"] not in APPLICABLE_RESOURCES:
- return {
- "compliance_type": "NOT_APPLICABLE",
- "annotation": "The rule doesn't apply to resources of type " +
- configuration_item["resourceType"] + "."
- }
-
- if configuration_item["configurationItemStatus"] == "ResourceDeleted":
- return {
- "compliance_type": "NOT_APPLICABLE",
- "annotation": "The configurationItem was deleted and therefore cannot be validated."
- }
-
-
- if configuration_item["resourceType"] == "AWS::Lambda::Function":
- client = boto3.client('lambda')
- all_tags = client.list_tags(Resource=configuration_item["ARN"])
- current_tags = all_tags['Tags'] # get only user tags.
-
-
- violation = find_violation(current_tags, rule_parameters)
-
- if violation:
- return {
- "compliance_type": "NON_COMPLIANT",
- "annotation": violation
- }
-
- return {
- "compliance_type": "COMPLIANT",
- "annotation": "This resource is compliant with the rule."
- }
-
-def lambda_handler(event, context):
- invoking_event = json.loads(event["invokingEvent"])
-
- configuration_item = invoking_event["configurationItem"]
-
- rule_parameters = json.loads(event["ruleParameters"])
-
- result_token = "No token found."
- if "resultToken" in event:
- result_token = event["resultToken"]
-
- evaluation = evaluate_compliance(configuration_item, rule_parameters)
-
- config = boto3.client("config")
- config.put_evaluations(
- Evaluations=[
- {
- "ComplianceResourceType":
- configuration_item["resourceType"],
- "ComplianceResourceId":
- configuration_item["resourceId"],
- "ComplianceType":
- evaluation["compliance_type"],
- "Annotation":
- evaluation["annotation"],
- "OrderingTimestamp":
- configuration_item["configurationItemCaptureTime"]
- },
- ],
- ResultToken=result_token
- )
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/pylintrc b/vendor/github.com/awslabs/aws-config-rules/python/pylintrc
deleted file mode 100644
index dc1fa25..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/pylintrc
+++ /dev/null
@@ -1,398 +0,0 @@
-[MASTER]
-
-# Specify a configuration file.
-#rcfile=
-
-# Python code to execute, usually for sys.path manipulation such as
-# pygtk.require().
-#init-hook=
-
-# Add files or directories to the blacklist. They should be base names, not
-# paths.
-ignore=third_party,ACM_CERTIFICATE_EXPIRATION_CHECK,AMI_OUTDATED_CHECK,API_GW_NOT_EDGE_OPTIMISED,API_GW_PRIVATE_RESTRICTED,API_GW_RESTRICTED_IP,CLOUDFRONT_LOGGING_ENABLED,CLOUDTRAIL_ENABLED_V2,CMK_BACKING_KEY_ROTATION_ENABLED,DYNAMODB_ENCRYPTED_CUSTOM,DYNAMODB_TABLE_ENCRYPTION_ENABLED,EBS_ENCRYPTED_VOLUMES_V2,EBS_SNAPSHOT_PUBLIC_RESTORABLE_CHECK,EC2_INSTANCE_MANAGED_BY_SSM,EC2_MANAGEDINSTANCE_ASSOCIATION_COMPLIANCE_STATUS_CHECK,EC2_MANAGEDINSTANCE_PATCH_COMPLIANCE_STATUS_CHECK,EC2_TAG_MATCHES_INSTANCE_PROFILE_NAME,ELB_ALB_PREDEFINED_SSL_CHECK,ELB_LOGGING_ENABLED,EMR_KERBEROS_ENABLED,GUARDDUTY_ENABLED_CENTRALIZED,IAM_ACCESS_KEY_ROTATED,IAM_GROUP_NO_POLICY_FULL_STAR,IAM_NO_USER,IAM_PASSWORD_POLICY,IAM_POLICY_REQUIRED,IAM_ROLE_NO_POLICY_FULL_STAR,IAM_USER_MFA_ENABLED,IAM_USER_NO_POLICY_FULL_STAR,IAM_USER_PERMISSION_BOUNDARY_CHECK,IAM_USER_USED_LAST_90_DAYS,INSTANCE_PROFILE_HAVE_DEFINED_POLICIES,INTERNET_GATEWAY_AUTHORIZED_ONLY,LAMBDA_CODE_IS_VERSIONED,LAMBDA_ROLE_ALLOWED_ON_LOGGING,RDS_INSTANCE_PUBLIC_ACCESS_CHECK,RDS_SNAPSHOTS_PUBLIC_PROHIBITED,RDS_STORAGE_ENCRYPTED,REDSHIFT_CLUSTER_CONFIGURATION_CHECK,REDSHIFT_CLUSTER_PUBLIC_ACCESS_CHECK,ROOT_ACCOUNT_MFA_ENABLED,ROOT_NO_ACCESS_KEY,S3_BUCKET_LOGGING_ENABLED,S3_BUCKET_PUBLIC_READ_PROHIBITED,S3_BUCKET_SSL_REQUESTS_ONLY,S3_BUCKET_VERSIONING_ENABLED,VPC_DEFAULT_SECURITY_GROUP_CLOSED,VPC_FLOW_LOGS_ENABLED,VPC_FLOW_LOGS_ENABLED_CUSTOM,cloudtrail_encrypted.py,cloudtrail_lfi_activated.py,config_enabled.py,config_rules_exist.py,ec2-exposed-instance.py,ec2_desired_instance_type.py,ec2_desired_lifecycle_spot.py,ec2_launch_wizard_security_group_prohibited.py,ec2_no_internet_access.py,ec2_require_ebs_snapshots_for_volumes.py,ec2_require_security_group_by_tag.py,ec2_require_tags_with_valid_values.py,ec2_security_group_ingress.py,ec2_security_group_port_range_all_prohibited.py,ec2_security_group_protocol_all_prohibited.py,ec2_vpc_public_subnet.py,iam_mfa_for_console_access.py,iam_policy_exists.py,iam_unused_keys.py,lambda_require_tags_with_valid_values.py,rds_desired_instance_type.py,rds_vpc_public_subnet.py,s3_bucket_default_encryption_enabled.py,s3_bucket_policy_prohibited.py
-
-# Add files or directories matching the regex patterns to the blacklist. The
-# regex matches against base names, not paths.
-ignore-patterns=object_detection_grpc_client.py,prediction_pb2.py,prediction_pb2_grpc.py,mnist_DDP.py,mnistddpserving.py
-
-# Pickle collected data for later comparisons.
-persistent=no
-
-# List of plugins (as comma separated values of python modules names) to load,
-# usually to register additional checkers.
-load-plugins=
-
-# Use multiple processes to speed up Pylint.
-jobs=4
-
-# Allow loading of arbitrary C extensions. Extensions are imported into the
-# active Python interpreter and may run arbitrary code.
-unsafe-load-any-extension=no
-
-# A comma-separated list of package or module names from where C extensions may
-# be loaded. Extensions are loading into the active Python interpreter and may
-# run arbitrary code
-extension-pkg-whitelist=
-
-
-[MESSAGES CONTROL]
-
-# Only show warnings with the listed confidence levels. Leave empty to show
-# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
-confidence=
-
-# Enable the message, report, category or checker with the given id(s). You can
-# either give multiple identifier separated by comma (,) or put this option
-# multiple time (only on the command line, not in the configuration file where
-# it should appear only once). See also the "--disable" option for examples.
-#enable=
-
-# Disable the message, report, category or checker with the given id(s). You
-# can either give multiple identifiers separated by comma (,) or put this
-# option multiple times (only on the command line, not in the configuration
-# file where it should appear only once).You can also use "--disable=all" to
-# disable everything first and then reenable specific checks. For example, if
-# you want to run only the similarities checker, you can use "--disable=all
-# --enable=similarities". If you want to run only the classes checker, but have
-# no Warning level messages displayed, use"--disable=all --enable=classes
-# --disable=W"
-disable=missing-docstring,line-too-long,global-statement,global-variable-undefined,bare-except,too-many-return-statements,too-many-branches,too-many-statements,too-many-locals,broad-except,unused-argument
-
-
-[REPORTS]
-
-# Set the output format. Available formats are text, parseable, colorized, msvs
-# (visual studio) and html. You can also give a reporter class, eg
-# mypackage.mymodule.MyReporterClass.
-output-format=text
-
-# Put messages in a separate file for each module / package specified on the
-# command line instead of printing them on stdout. Reports (if any) will be
-# written in a file name "pylint_global.[txt|html]". This option is deprecated
-# and it will be removed in Pylint 2.0.
-files-output=no
-
-# Tells whether to display a full report or only the messages
-reports=no
-
-# Python expression which should return a note less than 10 (10 is the highest
-# note). You have access to the variables errors warning, statement which
-# respectively contain the number of errors / warnings messages and the total
-# number of statements analyzed. This is used by the global evaluation report
-# (RP0004).
-evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
-
-# Template used to display messages. This is a python new-style format string
-# used to format the message information. See doc for all details
-#msg-template=
-
-
-[BASIC]
-
-# Good variable names which should always be accepted, separated by a comma
-good-names=i,j,k,ex,Run,_,v
-
-# Bad variable names which should always be refused, separated by a comma
-bad-names=foo,bar,baz,toto,tutu,tata,list
-
-# Colon-delimited sets of names that determine each other's naming style when
-# the name regexes allow several styles.
-name-group=
-
-# Include a hint for the correct naming format with invalid-name
-include-naming-hint=no
-
-# List of decorators that produce properties, such as abc.abstractproperty. Add
-# to this list to register other decorators that produce valid properties.
-property-classes=abc.abstractproperty
-
-# Regular expression matching correct function names
-function-rgx=[a-z_][a-z0-9_]{5,}$
-
-# Naming hint for function names
-function-name-hint=[a-z_][a-z0-9_]{5,}$
-
-# Regular expression matching correct variable names
-variable-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Naming hint for variable names
-variable-name-hint=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression matching correct constant names
-const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
-
-# Naming hint for constant names
-const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
-
-# Regular expression matching correct attribute names
-attr-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Naming hint for attribute names
-attr-name-hint=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression matching correct argument names
-argument-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Naming hint for argument names
-argument-name-hint=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression matching correct class attribute names
-class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
-
-# Naming hint for class attribute names
-class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
-
-# Regular expression matching correct inline iteration names
-inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
-
-# Naming hint for inline iteration names
-inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
-
-# Regular expression matching correct class names
-class-rgx=[A-Z_][a-zA-Z0-9_]+$
-
-# Naming hint for class names
-class-name-hint=[A-Z_][a-zA-Z0-9_]+$
-
-# Regular expression matching correct module names
-module-rgx=(?=.{2,128}$)([A-Z][A-Z_0-9]+[A-Z0-9](_test)?)$
-
-# Naming hint for module names
-module-name-hint=(?=.{2,128}$)([A-Z][A-Z_0-9]+[A-Z0-9](_test)?)$
-
-# Regular expression matching correct method names
-method-rgx=[a-z_][a-z0-9_]{5,}$
-
-# Naming hint for method names
-method-name-hint=[a-z_][a-z0-9_]{5,}$
-
-# Regular expression which should only match function or class names that do
-# not require a docstring.
-no-docstring-rgx=^_
-
-# Minimum line length for functions/classes that require docstrings, shorter
-# ones are exempt.
-docstring-min-length=-1
-
-
-[ELIF]
-
-# Maximum number of nested blocks for function / method body
-max-nested-blocks=5
-
-
-[TYPECHECK]
-
-# Tells whether missing members accessed in mixin class should be ignored. A
-# mixin class is detected if its name ends with "mixin" (case insensitive).
-ignore-mixin-members=yes
-
-# List of module names for which member attributes should not be checked
-# (useful for modules/projects where namespaces are manipulated during runtime
-# and thus existing member attributes cannot be deduced by static analysis. It
-# supports qualified module names, as well as Unix pattern matching.
-ignored-modules=
-
-# List of class names for which member attributes should not be checked (useful
-# for classes with dynamically set attributes). This supports the use of
-# qualified names.
-ignored-classes=optparse.Values,thread._local,_thread._local
-
-# List of members which are set dynamically and missed by pylint inference
-# system, and so shouldn't trigger E1101 when accessed. Python regular
-# expressions are accepted.
-generated-members=
-
-# List of decorators that produce context managers, such as
-# contextlib.contextmanager. Add to this list to register other decorators that
-# produce valid context managers.
-contextmanager-decorators=contextlib.contextmanager
-
-
-[FORMAT]
-
-# Maximum number of characters on a single line.
-max-line-length=100
-
-# Regexp for a line that is allowed to be longer than the limit.
-ignore-long-lines=^\s*(# )??$
-
-# Allow the body of an if to be on the same line as the test if there is no
-# else.
-single-line-if-stmt=no
-
-# List of optional constructs for which whitespace checking is disabled. `dict-
-# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
-# `trailing-comma` allows a space between comma and closing bracket: (a, ).
-# `empty-line` allows space-only lines.
-no-space-check=trailing-comma,dict-separator
-
-# Maximum number of lines in a module
-max-module-lines=1000
-
-# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
-# tab).
-indent-string=' '
-
-# Number of spaces of indent required inside a hanging or continued line.
-indent-after-paren=4
-
-# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
-expected-line-ending-format=
-
-
-[MISCELLANEOUS]
-
-# List of note tags to take in consideration, separated by a comma.
-notes=FIXME,XXX,TODO
-
-
-[VARIABLES]
-
-# Tells whether we should check for unused import in __init__ files.
-init-import=no
-
-# A regular expression matching the name of dummy variables (i.e. expectedly
-# not used).
-dummy-variables-rgx=(_+[a-zA-Z0-9]*?$)|dummy
-
-# List of additional names supposed to be defined in builtins. Remember that
-# you should avoid to define new builtins when possible.
-additional-builtins=
-
-# List of strings which can identify a callback function by name. A callback
-# name must start or end with one of those strings.
-callbacks=cb_,_cb
-
-# List of qualified module names which can have objects that can redefine
-# builtins.
-redefining-builtins-modules=six.moves,future.builtins
-
-
-[LOGGING]
-
-# Logging modules to check that the string format arguments are in logging
-# function parameter format
-logging-modules=logging
-
-
-[SIMILARITIES]
-
-# Minimum lines number of a similarity.
-min-similarity-lines=4
-
-# Ignore comments when computing similarities.
-ignore-comments=yes
-
-# Ignore docstrings when computing similarities.
-ignore-docstrings=yes
-
-# Ignore imports when computing similarities.
-ignore-imports=no
-
-
-[SPELLING]
-
-# Spelling dictionary name. Available dictionaries: none. To make it working
-# install python-enchant package.
-spelling-dict=
-
-# List of comma separated words that should not be checked.
-spelling-ignore-words=
-
-# A path to a file that contains private dictionary; one word per line.
-spelling-private-dict-file=
-
-# Tells whether to store unknown words to indicated private dictionary in
-# --spelling-private-dict-file option instead of raising a message.
-spelling-store-unknown-words=no
-
-
-[IMPORTS]
-
-# Deprecated modules which should not be used, separated by a comma
-deprecated-modules=regsub,TERMIOS,Bastion,rexec
-
-# Create a graph of every (i.e. internal and external) dependencies in the
-# given file (report RP0402 must not be disabled)
-import-graph=
-
-# Create a graph of external dependencies in the given file (report RP0402 must
-# not be disabled)
-ext-import-graph=
-
-# Create a graph of internal dependencies in the given file (report RP0402 must
-# not be disabled)
-int-import-graph=
-
-# Force import order to recognize a module as part of the standard
-# compatibility libraries.
-known-standard-library=
-
-# Force import order to recognize a module as part of a third party library.
-known-third-party=enchant
-
-# Analyse import fallback blocks. This can be used to support both Python 2 and
-# 3 compatible code, which means that the block might have code that exists
-# only in one or another interpreter, leading to false positives when analysed.
-analyse-fallback-blocks=no
-
-
-[DESIGN]
-
-# Maximum number of arguments for function / method
-max-args=7
-
-# Argument names that match this expression will be ignored. Default to name
-# with leading underscore
-ignored-argument-names=_.*
-
-# Maximum number of locals for function / method body
-max-locals=15
-
-# Maximum number of return / yield for function / method body
-max-returns=6
-
-# Maximum number of branch for function / method body
-max-branches=12
-
-# Maximum number of statements in function / method body
-max-statements=50
-
-# Maximum number of parents for a class (see R0901).
-max-parents=7
-
-# Maximum number of attributes for a class (see R0902).
-max-attributes=7
-
-# Minimum number of public methods for a class (see R0903).
-min-public-methods=0
-
-# Maximum number of public methods for a class (see R0904).
-max-public-methods=20
-
-# Maximum number of boolean expressions in a if statement
-max-bool-expr=5
-
-
-[CLASSES]
-
-# List of method names used to declare (i.e. assign) instance attributes.
-defining-attr-methods=__init__,__new__,setUp
-
-# List of valid names for the first argument in a class method.
-valid-classmethod-first-arg=cls
-
-# List of valid names for the first argument in a metaclass class method.
-valid-metaclass-classmethod-first-arg=mcs
-
-# List of member names, which should be excluded from the protected access
-# warning.
-exclude-protected=_asdict,_fields,_replace,_source,_make
-
-
-[EXCEPTIONS]
-
-# Exceptions that will emit a warning when being caught. Defaults to
-# "Exception"
-overgeneral-exceptions=Exception
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/rds_desired_instance_type.py b/vendor/github.com/awslabs/aws-config-rules/python/rds_desired_instance_type.py
deleted file mode 100644
index 7ad8859..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/rds_desired_instance_type.py
+++ /dev/null
@@ -1,56 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Ensure all RDS DB Instances are of a Given Type
-# Description: Checks that all RDS DB instances are of the type specified
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: RDS::DBInstance
-# Required Parameter: DBInstance
-# Example Value: db.t2.small
-#
-# See https://aws.amazon.com/ec2/instance-types/ for more instance types
-
-import boto3
-import json
-
-def is_applicable(config_item, event):
- status = config_item['configurationItemStatus']
- event_left_scope = event['eventLeftScope']
- test = ((status in ['OK', 'ResourceDiscovered']) and
- event_left_scope == False)
- return test
-
-
-def evaluate_compliance(config_item, rule_parameters):
- if (config_item['resourceType'] != 'AWS::RDS::DBInstance'):
- return 'NOT_APPLICABLE'
-
- elif (config_item['configuration']['dBInstanceClass'] in
- rule_parameters['DBInstance']):
- return 'COMPLIANT'
- else:
- return 'NON_COMPLIANT'
-
-
-def lambda_handler(event, context):
- invoking_event = json.loads(event['invokingEvent'])
- rule_parameters = json.loads(event['ruleParameters'])
-
- compliance_value = 'NOT_APPLICABLE'
-
- if is_applicable(invoking_event['configurationItem'], event):
- compliance_value = evaluate_compliance(
- invoking_event['configurationItem'], rule_parameters)
-
- config = boto3.client('config')
- response = config.put_evaluations(
- Evaluations=[
- {
- 'ComplianceResourceType': invoking_event['configurationItem']['resourceType'],
- 'ComplianceResourceId': invoking_event['configurationItem']['resourceId'],
- 'ComplianceType': compliance_value,
- 'OrderingTimestamp': invoking_event['configurationItem']['configurationItemCaptureTime']
- },
- ],
- ResultToken=event['resultToken'])
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/rds_vpc_public_subnet.py b/vendor/github.com/awslabs/aws-config-rules/python/rds_vpc_public_subnet.py
deleted file mode 100644
index 49e354c..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/rds_vpc_public_subnet.py
+++ /dev/null
@@ -1,108 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Description: Check that no RDS Instances are in Public Subnet
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: RDS:DBInstance
-# Accepted Parameters: None
-# Your Lambda function execution role will need to have a policy that provides the appropriate
-# permissions. Here is a policy that you can consider. You should validate this for your own
-# environment
-#{
-# "Version": "2012-10-17",
-# "Statement": [
-# {
-# "Effect": "Allow",
-# "Action": [
-# "logs:CreateLogGroup",
-# "logs:CreateLogStream",
-# "logs:PutLogEvents"
-# ],
-# "Resource": "arn:aws:logs:*:*:*"
-# },
-# {
-# "Effect": "Allow",
-# "Action": [
-# "config:PutEvaluations",
-# "ec2:DescribeRouteTables"
-# ],
-# "Resource": "*"
-# }
-# ]
-#}
-#
-
-import boto3
-import botocore
-import json
-import logging
-
-log = logging.getLogger()
-log.setLevel(logging.INFO)
-
-def evaluate_compliance(configuration_item):
- vpc_id = configuration_item["configuration"]['dBSubnetGroup']["vpcId"]
- subnet_ids = []
- for i in configuration_item["configuration"]['dBSubnetGroup']['subnets']:
- subnet_ids.append(i['subnetIdentifier'])
- client = boto3.client("ec2");
-
- response = client.describe_route_tables()
-
- # If the subnet is explicitly associated to a route table, check if there
- # is a public route. If no explicit association exists, check if the main
- # route table has a public route.
-
- private = True
-
- for subnet_id in subnet_ids:
- mainTableIsPublic = False
- noExplicitAssociationFound = True
- explicitAssocationIsPublic = False
- for i in response['RouteTables']:
- if i['VpcId'] == vpc_id:
- for j in i['Associations']:
- if j['Main'] == True:
- for k in i['Routes']:
- if k['DestinationCidrBlock'] == '0.0.0.0/0' or k['GatewayId'].startswith('igw-'):
- mainTableIsPublic = True
- else:
- if j['SubnetId'] == subnet_id:
- noExplicitAssociationFound = False
- for k in i['Routes']:
- if k['DestinationCidrBlock'] == '0.0.0.0/0' or k['GatewayId'].startswith('igw-'):
- explicitAssocationIsPublic = True
-
- if (mainTableIsPublic and noExplicitAssociationFound) or explicitAssocationIsPublic:
- private = False
-
- if private:
- return {
- "compliance_type": "COMPLIANT",
- "annotation": 'Its in private subnet'
- }
- else:
- return {
- "compliance_type" : "NON_COMPLIANT",
- "annotation" : 'Not in private subnet'
- }
-
-def lambda_handler(event, context):
- log.debug('Event %s', event)
- invoking_event = json.loads(event['invokingEvent'])
- configuration_item = invoking_event["configurationItem"]
- evaluation = evaluate_compliance(configuration_item)
- config = boto3.client('config')
-
- response = config.put_evaluations(
- Evaluations=[
- {
- 'ComplianceResourceType': invoking_event['configurationItem']['resourceType'],
- 'ComplianceResourceId': invoking_event['configurationItem']['resourceId'],
- 'ComplianceType': evaluation["compliance_type"],
- "Annotation": evaluation["annotation"],
- 'OrderingTimestamp': invoking_event['configurationItem']['configurationItemCaptureTime']
- },
- ],
- ResultToken=event['resultToken'])
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/s3_bucket_default_encryption_enabled.py b/vendor/github.com/awslabs/aws-config-rules/python/s3_bucket_default_encryption_enabled.py
deleted file mode 100644
index 9b03a82..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/s3_bucket_default_encryption_enabled.py
+++ /dev/null
@@ -1,159 +0,0 @@
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Description: Check that S3 buckets have default encryption enabled.
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: S3:Bucket
-# Accepted Parameters: None
-# Your Lambda function execution role will need to have a policy that provides
-# the appropriate permissions. Here is a policy that you can consider.
-# You should validate this for your own environment.
-#
-# Optional Parameters:
-# 1. Key: SSE_OR_KMS
-# Values: SSE, KMS
-# 2. Key: KMS_ARN
-# Value: ARN of the KMS key
-#
-# NOTE: If you specify KMS_ARN, you must choose KMS for SSE_OR_KMS.
-#
-# {
-# "Version": "2012-10-17",
-# "Statement": [
-# {
-# "Effect": "Allow",
-# "Action": [
-# "logs:CreateLogGroup",
-# "logs:CreateLogStream",
-# "logs:PutLogEvents"
-# ],
-# "Resource": "arn:aws:logs:*:*:*"
-# },
-# {
-# "Effect": "Allow",
-# "Action": [
-# "config:PutEvaluations"
-# ],
-# "Resource": "*"
-# },
-# {
-# "Effect": "Allow",
-# "Action": [
-# "s3:GetEncryptionConfiguration"
-# ],
-# "Resource": "arn:aws:s3:::*"
-# }
-# ]
-# }
-
-
-import boto3
-import json
-
-
-s3 = boto3.client("s3")
-config = boto3.client('config')
-
-
-APPLICABLE_RESOURCES = ["AWS::S3::Bucket"]
-
-
-def evaluate_compliance(configuration_item, rule_parameters):
-
- # Start as non-compliant
- compliance_type = 'NON_COMPLIANT'
- annotation = "S3 bucket either does NOT have default encryption enabled, " \
- + "has the wrong TYPE of encryption enabled, or is encrypted " \
- + "with the wrong KMS key."
-
- # Check if resource was deleted
- if configuration_item['configurationItemStatus'] == "ResourceDeleted":
- compliance_type = 'NOT_APPLICABLE'
- annotation = "The resource was deleted."
-
- # Check resource for applicability
- elif configuration_item["resourceType"] not in APPLICABLE_RESOURCES:
- compliance_type = 'NOT_APPLICABLE'
- annotation = "The rule doesn't apply to resources of type " \
- + configuration_item["resourceType"] + "."
-
- # Check bucket for default encryption
- else:
- try:
- # Encryption isn't in configurationItem so an API call is necessary
- response = s3.get_bucket_encryption(
- Bucket=configuration_item["resourceName"]
- )
-
- # Check if optional parameters were supplied
- if 'SSE_OR_KMS' in rule_parameters:
- if rule_parameters['SSE_OR_KMS'] == 'SSE':
- if response['ServerSideEncryptionConfiguration']['Rules'][0]['ApplyServerSideEncryptionByDefault']['SSEAlgorithm'] != 'AES256':
- compliance_type = 'NON_COMPLIANT'
- annotation = 'S3 bucket is NOT encrypted with SSE-S3.'
- else:
- compliance_type = 'COMPLIANT'
- annotation = 'S3 bucket is encrypted with SSE-S3.'
- if rule_parameters['SSE_OR_KMS'] == 'KMS':
- if response['ServerSideEncryptionConfiguration']['Rules'][0]['ApplyServerSideEncryptionByDefault']['SSEAlgorithm'] != 'aws:kms':
- compliance_type = 'NON_COMPLIANT'
- annotation = 'S3 bucket is NOT encrypted with KMS.'
- else:
- if 'KMS_ARN' in rule_parameters:
- if rule_parameters['KMS_ARN'] != response['ServerSideEncryptionConfiguration']['Rules'][0]['ApplyServerSideEncryptionByDefault']['KMSMasterKeyID']:
- compliance_type = 'NON_COMPLIANT'
- annotation = 'S3 bucket is encrypted with the wrong KMS key.'
- else:
- compliance_type = 'COMPLIANT'
- annotation = 'S3 bucket is encrypted with the correct KMS key.'
- # KMS but no ARN is specified
- else:
- compliance_type = 'COMPLIANT'
- annotation = 'S3 bucket is encrypted with KMS.'
- # If we received no parameters and we made it this far, we're compliant.
- else:
- compliance_type = 'COMPLIANT'
- annotation = 'S3 bucket has default encryption enabled.'
-
- except:
- # If we receive an error, the default encryption flag is not set
- compliance_type = 'NON_COMPLIANT'
- annotation = 'S3 bucket does NOT have default encryption enabled.'
-
- return {
- "compliance_type": compliance_type,
- "annotation": annotation
- }
-
-
-def lambda_handler(event, context):
-
- invoking_event = json.loads(event['invokingEvent'])
-
- # Check for oversized item
- if "configurationItem" in invoking_event:
- configuration_item = invoking_event["configurationItem"]
- elif "configurationItemSummary" in invokingEvent:
- configuration_item = invoking_event["configurationItemSummary"]
-
- # Optional parameters
- rule_parameters = {}
- if 'ruleParameters' in event:
- rule_parameters = json.loads(event['ruleParameters'])
-
- evaluation = evaluate_compliance(configuration_item, rule_parameters)
-
- print('Compliance evaluation for %s: %s' % (configuration_item['resourceId'], evaluation["compliance_type"]))
- print('Annotation: %s' % (evaluation["annotation"]))
-
- response = config.put_evaluations(
- Evaluations=[
- {
- 'ComplianceResourceType': invoking_event['configurationItem']['resourceType'],
- 'ComplianceResourceId': invoking_event['configurationItem']['resourceId'],
- 'ComplianceType': evaluation["compliance_type"],
- "Annotation": evaluation["annotation"],
- 'OrderingTimestamp': invoking_event['configurationItem']['configurationItemCaptureTime']
- },
- ],
- ResultToken=event['resultToken'])
diff --git a/vendor/github.com/awslabs/aws-config-rules/python/s3_bucket_policy_prohibited.py b/vendor/github.com/awslabs/aws-config-rules/python/s3_bucket_policy_prohibited.py
deleted file mode 100644
index a8c4dea..0000000
--- a/vendor/github.com/awslabs/aws-config-rules/python/s3_bucket_policy_prohibited.py
+++ /dev/null
@@ -1,90 +0,0 @@
-#
-# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
-#
-# Description: Check if any s3 bucket has bucket policy and if it does mark it non-compliant
-#
-# Trigger Type: Change Triggered
-# Scope of Changes: S3:Instance
-# Accepted Parameters: None
-# Your Lambda function execution role will need to have a policy that provides the appropriate
-# permissions. Here is a policy that you can consider. You should validate this for your own
-# environment
-#{
-# "Version": "2012-10-17",
-# "Statement": [
-# {
-# "Effect": "Allow",
-# "Action": [
-# "logs:CreateLogGroup",
-# "logs:CreateLogStream",
-# "logs:PutLogEvents"
-# ],
-# "Resource": "arn:aws:logs:*:*:*"
-# },
-# {
-# "Effect": "Allow",
-# "Action": [
-# "config:PutEvaluations"
-# ],
-# "Resource": "*"
-# }
-# ]
-#}
-#
-
-import boto3
-import json
-import logging
-
-log = logging.getLogger()
-log.setLevel(logging.DEBUG)
-APPLICABLE_RESOURCES = ["AWS::S3::Bucket"]
-
-
-def evaluate_compliance(configuration_item):
- if configuration_item["resourceType"] not in APPLICABLE_RESOURCES:
- return {
- "compliance_type": "NOT_APPLICABLE",
- "annotation": "The rule doesn't apply to resources of type " +
- configuration_item["resourceType"] + "."
- }
-
- if configuration_item['configurationItemStatus'] == "ResourceDeleted":
- return {
- "compliance_type": "NOT_APPLICABLE",
- "annotation": "The configurationItem was deleted " +
- "and therefore cannot be validated"
- }
-
- bucket_policy = configuration_item["supplementaryConfiguration"].get("BucketPolicy")
- if bucket_policy['policyText'] is None:
- return {
- "compliance_type": "COMPLIANT",
- "annotation": 'Bucket Policy does not exists'
- }
-
- else:
- return {
- "compliance_type": "NON_COMPLIANT",
- "annotation": 'Bucket Policy exists'
- }
-
-
-def lambda_handler(event, context):
- log.debug('Event %s', event)
- invoking_event = json.loads(event['invokingEvent'])
- configuration_item = invoking_event["configurationItem"]
- evaluation = evaluate_compliance(configuration_item)
- config = boto3.client('config')
-
- config.put_evaluations(
- Evaluations=[
- {
- 'ComplianceResourceType': invoking_event['configurationItem']['resourceType'],
- 'ComplianceResourceId': invoking_event['configurationItem']['resourceId'],
- 'ComplianceType': evaluation["compliance_type"],
- "Annotation": evaluation["annotation"],
- 'OrderingTimestamp': invoking_event['configurationItem']['configurationItemCaptureTime']
- },
- ],
- ResultToken=event['resultToken'])
diff --git a/versions.tf b/versions.tf
index d9b6f79..6b6318d 100644
--- a/versions.tf
+++ b/versions.tf
@@ -1,3 +1,3 @@
terraform {
- required_version = ">= 0.12"
+ required_version = ">= 0.13"
}