generated from plus3it/repo-template
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0f73aa0
commit e8a4816
Showing
19 changed files
with
1,465 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,66 @@ | ||
# terraform-aws-tardigrade-iam-key-enforcer | ||
Module to manage IAM key enforcer | ||
# Tardigrade IAM Key Enforcer | ||
|
||
This repo contains the Python-based Lambda function that will audit IAM Access keys for an account and will enforce key rotation as well as notify users. | ||
|
||
## Basic Function | ||
|
||
The Lambda function is triggered for each account by an Event notification that is configured to run on a schedule. | ||
The function audits each user in an account for access keys and determines how long before they expire, it will then notify users that their key expires in X days and that automatic key enforcement is forthcoming. | ||
|
||
<!-- BEGIN TFDOCS --> | ||
## Requirements | ||
|
||
| Name | Version | | ||
|------|---------| | ||
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.1 | | ||
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 3.0 | | ||
|
||
## Providers | ||
|
||
| Name | Version | | ||
|------|---------| | ||
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 3.0 | | ||
|
||
## Resources | ||
|
||
| Name | Type | | ||
|------|------| | ||
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | | ||
| [aws_iam_policy_document.lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | ||
| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | | ||
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | | ||
|
||
## Inputs | ||
|
||
| Name | Description | Type | Default | Required | | ||
|------|-------------|------|---------|:--------:| | ||
| <a name="input_admin_email"></a> [admin\_email](#input\_admin\_email) | Admin Email that will receive all emails and reports about actions taken if email is enabled | `string` | n/a | yes | | ||
| <a name="input_assume_role_name"></a> [assume\_role\_name](#input\_assume\_role\_name) | Name of the IAM role that the lambda will assume in the target account | `string` | n/a | yes | | ||
| <a name="input_email_source"></a> [email\_source](#input\_email\_source) | Email that will be used to send messages | `string` | n/a | yes | | ||
| <a name="input_key_age_delete"></a> [key\_age\_delete](#input\_key\_age\_delete) | Age at which a key should be deleted (e.g. 120) | `number` | n/a | yes | | ||
| <a name="input_key_age_inactive"></a> [key\_age\_inactive](#input\_key\_age\_inactive) | Age at which a key should be inactive (e.g. 90) | `number` | n/a | yes | | ||
| <a name="input_key_age_warning"></a> [key\_age\_warning](#input\_key\_age\_warning) | Age at which to warn (e.g. 75) | `number` | n/a | yes | | ||
| <a name="input_key_use_threshold"></a> [key\_use\_threshold](#input\_key\_use\_threshold) | Age at which unused keys should be deleted (e.g.30) | `number` | n/a | yes | | ||
| <a name="input_accounts"></a> [accounts](#input\_accounts) | List of account objects to create events for | <pre>list(object({<br> account_name = string<br> account_number = string<br> role_name = optional(string) # deprecated<br> armed = bool<br> debug = optional(bool, false)<br> email_user_enabled = bool<br> email_targets = list(string)<br> exempt_groups = list(string)<br> schedule_expression = optional(string, "cron(0 1 ? * SUN *)")<br><br> }))</pre> | `[]` | no | | ||
| <a name="input_email_admin_report_enabled"></a> [email\_admin\_report\_enabled](#input\_email\_admin\_report\_enabled) | Used to enable or disable the SES emailed report | `bool` | `false` | no | | ||
| <a name="input_email_admin_report_subject"></a> [email\_admin\_report\_subject](#input\_email\_admin\_report\_subject) | Subject of the report email that is sent | `string` | `null` | no | | ||
| <a name="input_email_banner_message"></a> [email\_banner\_message](#input\_email\_banner\_message) | Messages that will be at the top of all emails sent to notify recipients of important information | `string` | `""` | no | | ||
| <a name="input_email_banner_message_color"></a> [email\_banner\_message\_color](#input\_email\_banner\_message\_color) | Color of email banner message, must be valid html color | `string` | `"red"` | no | | ||
| <a name="input_email_tag"></a> [email\_tag](#input\_email\_tag) | Tag to be placed on the IAM user that we can use to notify when their key is going to be disabled/deleted | `string` | `"keyenforcer:email"` | no | | ||
| <a name="input_email_templates"></a> [email\_templates](#input\_email\_templates) | Email templates to use for Admin and User emails | <pre>object({<br> admin = optional(object({<br> subject = optional(string, null),<br> html = optional(string, null),<br> text = optional(string, null),<br> }), {}),<br> user = optional(object({<br> subject = optional(string, null),<br> html = optional(string, null),<br> text = optional(string, null),<br> }), {})<br> })</pre> | `{}` | no | | ||
| <a name="input_lambda"></a> [lambda](#input\_lambda) | Map of any additional arguments for the upstream lambda module. See <https://github.com/terraform-aws-modules/terraform-aws-lambda> | <pre>object({<br> artifacts_dir = optional(string, "builds")<br> build_in_docker = optional(bool, false)<br> create_package = optional(bool, true)<br> ephemeral_storage_size = optional(number)<br> ignore_source_code_hash = optional(bool, true)<br> local_existing_package = optional(string)<br> recreate_missing_package = optional(bool, false)<br> runtime = optional(string, "python3.11")<br> s3_bucket = optional(string)<br> s3_existing_package = optional(map(string))<br> s3_prefix = optional(string)<br> store_on_s3 = optional(bool, false)<br> timeout = optional(number, 300)<br> source_path = optional(object({<br> patterns = optional(list(string), ["!\\.terragrunt-source-manifest"])<br> }), {})<br> })</pre> | `{}` | no | | ||
| <a name="input_log_level"></a> [log\_level](#input\_log\_level) | Log level for lambda | `string` | `"INFO"` | no | | ||
| <a name="input_project_name"></a> [project\_name](#input\_project\_name) | Project name to prefix resources with | `string` | `"iam-key-enforcer"` | no | | ||
| <a name="input_s3_bucket"></a> [s3\_bucket](#input\_s3\_bucket) | Bucket name to write the audit report to if s3\_enabled is set to 'true' | `string` | `null` | no | | ||
| <a name="input_s3_enabled"></a> [s3\_enabled](#input\_s3\_enabled) | Set to 'true' and provide s3\_bucket if the audit report should be written to S3 | `bool` | `false` | no | | ||
| <a name="input_schedule_expression"></a> [schedule\_expression](#input\_schedule\_expression) | (DEPRECATED) Schedule Expressions for Rules | `string` | `null` | no | | ||
| <a name="input_tags"></a> [tags](#input\_tags) | Tags for resource | `map(string)` | `{}` | no | | ||
|
||
## Outputs | ||
|
||
| Name | Description | | ||
|------|-------------| | ||
| <a name="output_lambda"></a> [lambda](#output\_lambda) | The lambda module object | | ||
| <a name="output_queue"></a> [queue](#output\_queue) | The SQS Queue resource object | | ||
|
||
<!-- END TFDOCS --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
{{#if email_banner_msg}} | ||
<h1 style="color:{{email_banner_msg_color}}">{{email_banner_msg}}</h1> | ||
{{/if}} | ||
|
||
<h2>Expiring Access Key Report for {{account_number}} - {{account_name}}</h2> | ||
|
||
{{#if unarmed}} | ||
<h3 style="color:red"> | ||
The IAM Key Enforcer is not active and NO action has been taken on your key | ||
</h3> | ||
<p> | ||
The information below is for informational purposes and represents the results if the IAM Key Enforcer were active. | ||
</p> | ||
{{/if}} | ||
|
||
<p> | ||
Access Keys over {{key_age_inactive}} days old have been DEACTIVATED, keys older than {{key_age_delete}} days have | ||
been DELETED. | ||
Access keys over {{key_age_warning}} days old are DEACTIVATED at {{key_age_inactive}} days old and DELETED after | ||
{{key_age_delete}} days old. | ||
Rotate any keys as necessary to prevent disruption to your applications. | ||
</p> | ||
|
||
{{#if exempt_groups}} | ||
<p> | ||
Grayed out rows are exempt via membership in an exempt IAM Group(s):{{exempt_groups}}. | ||
<br /> | ||
Exempted group members also have a key status value of <STATUS> (Exempt). | ||
</p> | ||
{{/if}} | ||
|
||
<table> | ||
<thead> | ||
<tr> | ||
<th>IAM User Name</th> | ||
<th>Access Key ID</th> | ||
<th>Key Age</th> | ||
<th>Key Status</th> | ||
<th>Last Used</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{{#each key_report_contents}} | ||
<tr bgcolor="{{bg_color}}"> | ||
<td>{{user_name}}</td> | ||
<td>{{access_key_id}}</td> | ||
<td>{{key_age}}</td> | ||
<td>{{key_status}}</td> | ||
<td>{{last_used_date}}</td> | ||
</tr> | ||
{{/each}} | ||
</tbody> | ||
</table> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{{#if email_banner_msg}} | ||
{{email_banner_msg}} | ||
{{/if}} | ||
|
||
<h2>Expiring Access Key Report for {{account_number}} - {{account_name}}</h2> | ||
|
||
{{#if unarmed}} | ||
The IAM Key Enforcer is not active and NO action has been taken on your key | ||
|
||
The information below is for informational purposes and represents the results if the IAM Key Enforcer were active. | ||
{{/if}} | ||
|
||
Access Keys over {{key_age_inactive}} days old have been DEACTIVATED, keys older than {{key_age_delete}} days have been DELETED. | ||
Access keys over {{key_age_warning}} days old are DEACTIVATED at {{key_age_inactive}} days old and DELETED after {{key_age_delete}} days old. | ||
Rotate any keys as necessary to prevent disruption to your applications. | ||
|
||
|
||
{{#if exempt_groups}} | ||
Exempted IAM Group(s):{{exempt_groups}}. | ||
Exempted group members have a key status value of <STATUS> (Exempt). | ||
{{/if}} | ||
|
||
IAM User Name, Access Key ID, Key Age, Key Status, Last Used | ||
{{#each key_report_contents}} | ||
{{user_name}}, {{access_key_id}}, {{key_age}}, {{key_status}}, {{last_used_date}} | ||
{{/each}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{{#if email_banner_msg}} | ||
<h1 style="color:{{email_banner_msg_color}}">{{email_banner_msg}}</h1> | ||
{{/if}} | ||
|
||
<h2>Expiring Access Key Report for {{user_name}}</h2> | ||
|
||
{{#if unarmed}} | ||
<h3 style="color:red"> | ||
The IAM Key Enforcer is not active and NO action has been taken on your key | ||
</h3> | ||
<p> | ||
The information below is for informational purposes and represents the results if the IAM Key Enforcer were active. | ||
</p> | ||
{{/if}} | ||
|
||
<p>The access key {{access_key_id}} is over {{key_age}} days old and has been {{action}}.</p> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{{#if email_banner_msg}} | ||
{{email_banner_msg}} | ||
{{/if}} | ||
|
||
Expiring Access Key Report for {{user_name}} | ||
|
||
{{#if unarmed}} | ||
The IAM Key Enforcer is not active and NO action has been taken on your key | ||
|
||
The information below is for informational purposes and represents the results if the IAM Key Enforcer were active. | ||
{{/if}} | ||
|
||
The access key {{access_key_id}} is over {{key_age}} days old and has been {{action}}. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
data "aws_caller_identity" "current" {} | ||
data "aws_partition" "current" {} | ||
data "aws_region" "current" {} | ||
|
||
data "aws_iam_policy_document" "lambda" { | ||
statement { | ||
sid = "AllowS3Object" | ||
actions = [ | ||
"s3:PutObject", | ||
"s3:PutObjectTagging", | ||
"s3:PutObjectVersionTagging", | ||
] | ||
resources = ["arn:${data.aws_partition.current.partition}:s3:::${var.s3_bucket}/*"] | ||
} | ||
|
||
statement { | ||
actions = [ | ||
"ses:SendEmail", | ||
"ses:SendTemplatedEmail", | ||
"ses:TestRenderTemplate" | ||
] | ||
resources = [ | ||
"*" | ||
] | ||
} | ||
|
||
statement { | ||
sid = "AllowAssumeRole" | ||
actions = [ | ||
"sts:AssumeRole" | ||
] | ||
resources = [ | ||
"arn:${data.aws_partition.current.partition}:iam::*:role/${var.assume_role_name}" | ||
] | ||
} | ||
} | ||
|
||
module "lambda" { | ||
source = "git::https://github.com/terraform-aws-modules/terraform-aws-lambda.git?ref=v7.2.1" | ||
|
||
description = "Lambda function for Key Enforcement" | ||
function_name = var.project_name | ||
handler = "iam_key_enforcer.lambda_handler" | ||
tags = var.tags | ||
|
||
attach_policy_json = true | ||
policy_json = data.aws_iam_policy_document.lambda.json | ||
|
||
artifacts_dir = var.lambda.artifacts_dir | ||
build_in_docker = var.lambda.build_in_docker | ||
create_package = var.lambda.create_package | ||
ignore_source_code_hash = var.lambda.ignore_source_code_hash | ||
local_existing_package = var.lambda.local_existing_package | ||
recreate_missing_package = var.lambda.recreate_missing_package | ||
ephemeral_storage_size = var.lambda.ephemeral_storage_size | ||
runtime = var.lambda.runtime | ||
s3_bucket = var.lambda.s3_bucket | ||
s3_existing_package = var.lambda.s3_existing_package | ||
s3_prefix = var.lambda.s3_prefix | ||
store_on_s3 = var.lambda.store_on_s3 | ||
timeout = var.lambda.timeout | ||
|
||
environment_variables = { | ||
LOG_LEVEL = var.log_level | ||
EMAIL_ADMIN_REPORT_ENABLED = var.email_admin_report_enabled | ||
EMAIL_ADMIN_REPORT_SUBJECT = var.email_admin_report_subject | ||
EMAIL_SOURCE = var.email_source | ||
ADMIN_EMAIL = var.admin_email | ||
KEY_AGE_WARNING = var.key_age_warning | ||
KEY_AGE_INACTIVE = var.key_age_inactive | ||
KEY_AGE_DELETE = var.key_age_delete | ||
KEY_USE_THRESHOLD = var.key_use_threshold | ||
S3_ENABLED = var.s3_enabled | ||
S3_BUCKET = var.s3_bucket | ||
EMAIL_TAG = var.email_tag | ||
EMAIL_BANNER_MSG = var.email_banner_message | ||
EMAIL_BANNER_MSG_COLOR = var.email_banner_message_color | ||
EMAIL_USER_TEMPLATE = aws_ses_template.user_template.id | ||
EMAIL_ADMIN_TEMPLATE = aws_ses_template.admin_template.id | ||
} | ||
|
||
source_path = [ | ||
{ | ||
path = "${path.module}/src/python", | ||
prefix_in_zip = "" | ||
pip_requirements = true | ||
patterns = var.lambda.source_path.patterns | ||
}, | ||
] | ||
} | ||
|
||
resource "aws_lambda_permission" "this" { | ||
action = "lambda:InvokeFunction" | ||
function_name = module.lambda.lambda_function_name | ||
principal = "events.amazonaws.com" | ||
source_arn = "arn:${data.aws_partition.current.partition}:events:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:rule/${var.project_name}-*" | ||
} | ||
|
||
############################## | ||
# SQS Queue Policy | ||
############################## | ||
resource "aws_sqs_queue_policy" "this" { | ||
queue_url = aws_sqs_queue.this.id | ||
policy = jsonencode( | ||
{ | ||
Version = "2012-10-17", | ||
Id = "sqspolicy", | ||
Statement : [ | ||
{ | ||
Sid = "AllowSend", | ||
Effect = "Allow", | ||
Principal = "*", | ||
Action = "sqs:SendMessage", | ||
Resource = aws_sqs_queue.this.arn, | ||
Condition = { | ||
"ArnLike" : { | ||
"aws:SourceArn" : "arn:${data.aws_partition.current.partition}:events:*:*:rule/${var.project_name}-*" | ||
} | ||
} | ||
}, | ||
{ | ||
Sid = "AllowRead", | ||
Effect = "Allow", | ||
"Principal" : { | ||
"AWS" : "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:root" | ||
}, | ||
Action = "sqs:ReceiveMessage", | ||
Resource = aws_sqs_queue.this.arn, | ||
} | ||
] | ||
} | ||
) | ||
} | ||
|
||
############################## | ||
# SQS Queue | ||
############################## | ||
resource "aws_sqs_queue" "this" { | ||
name = "${var.project_name}-dlq" | ||
message_retention_seconds = 1209600 | ||
receive_wait_time_seconds = 20 | ||
visibility_timeout_seconds = 30 | ||
tags = var.tags | ||
} | ||
|
||
############################## | ||
# Schedule Event | ||
############################## | ||
module "scheduled_events" { | ||
source = "./modules/scheduled_event" | ||
for_each = { for account in var.accounts : account.account_name => account } | ||
|
||
event_name = each.value.account_name | ||
event_rule_description = "Scheduled Event that runs IAM Key Enforcer Lambda for account ${each.value.account_number} - ${each.value.account_name}" | ||
lambda_arn = module.lambda.lambda_function_arn | ||
project_name = var.project_name | ||
tags = var.tags | ||
schedule_expression = var.schedule_expression != null ? var.schedule_expression : each.value.schedule_expression | ||
|
||
|
||
dead_letter_config = { | ||
arn = aws_sqs_queue.this.arn | ||
} | ||
|
||
input_transformer = { | ||
input_template = jsonencode({ | ||
"account_number" : each.value.account_number, | ||
"account_name" : each.value.account_name, | ||
"role_arn" : "arn:${data.aws_partition.current.partition}:iam::${each.value.account_number}:role/${var.assume_role_name}", | ||
"armed" : each.value.armed, | ||
"debug" : each.value.debug, | ||
"email_targets" : each.value.email_targets, | ||
"exempt_groups" : each.value.exempt_groups, | ||
"email_user_enabled" : each.value.email_user_enabled, | ||
}) | ||
} | ||
} | ||
|
||
resource "aws_ses_template" "user_template" { | ||
name = "${var.project_name}-user" | ||
html = var.email_templates.user.html != null ? var.email_templates.user.html : file("${path.module}/email_templates/user_email.html") | ||
subject = var.email_templates.user.subject != null ? var.email_templates.user.subject : "IAM User Key {{armed_state_msg}} for {{user_name}}" | ||
text = var.email_templates.user.text != null ? var.email_templates.user.text : file("${path.module}/email_templates/user_email.txt") | ||
} | ||
|
||
resource "aws_ses_template" "admin_template" { | ||
name = "${var.project_name}-admin" | ||
html = var.email_templates.admin.html != null ? var.email_templates.admin.html : file("${path.module}/email_templates/admin_email.html") | ||
subject = var.email_templates.admin.subject != null ? var.email_templates.admin.subject : "IAM Key Enforcement Report for {{account_number}}" | ||
text = var.email_templates.admin.text != null ? var.email_templates.admin.text : file("${path.module}/email_templates/admin_email.txt") | ||
} |
Oops, something went wrong.