Skip to content

merge into master #4

Merged
merged 33 commits into from
Jan 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "aws-lambda-ddns-function"]
path = aws-lambda-ddns-function
url = https://github.com/aws-samples/aws-lambda-ddns-function
File renamed without changes.
9 changes: 5 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.48.0
rev: v1.62.3
hooks:
# - id: terraform_validate
- id: terraform_fmt
- id: terraform_docs_replace
args: ['table']
- id: terraform_docs
args:
- --args=--config=.terraform-docs.yml
exclude: common/*.tf
exclude: version.tf
exclude: examples/
- id: terraform_tflint
args: [ "--args=--config=__GIT_WORKING_DIR__/.tflint.hcl"]
exclude: examples/
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.4.0
rev: v4.1.0
hooks:
- id: check-symlinks
- id: detect-aws-credentials
Expand Down
45 changes: 45 additions & 0 deletions .terraform-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
formatter: markdown table

header-from: main.tf
footer-from: ""

sections:
## hide: []
show:
- data-sources
- header
- footer
- inputs
- modules
- outputs
- providers
- requirements
- resources

output:
file: README.md
mode: replace
# mode: inject
# template: |-
# <!-- BEGIN_TF_DOCS -->
# {{ .Content }}
# <!-- END_TF_DOCS -->

## output-values:
## enabled: false
## from: ""
##
## sort:
## enabled: true
## by: name
##
## settings:
## anchor: true
## color: true
## default: true
## description: false
## escape: true
## indent: 2
## required: true
## sensitive: true
## type: true
6 changes: 6 additions & 0 deletions .tflint.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,9 @@ rule "aws_instance_invalid_type" {
plugin "aws" {
enabled = true
}

# https://github.com/terraform-linters/tflint/blob/v0.33.1/docs/rules/terraform_module_pinned_source.md
rule "terraform_module_pinned_source" {
enabled = false
}

2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Versions

* v1.0.0 -- {{ yyyy-mm-dd }}
* v1.0.0 -- 2022-01-21
- initial creation

152 changes: 56 additions & 96 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,96 +1,56 @@
# aws-dynamic-route53

## About

This module will construct all the resources to allow for automated C2 DNS registration in Route53. This is largely sourced from the
AWS blog on [DNS in a Multiaccount Environment with Route53](https://aws.amazon.com/blogs/security/simplify-dns-management-in-a-multiaccount-environment-with-route-53-resolver/) . We have added to it to also do PTR registration, as well as
making it IPv6 ready.

The [code](https://github.com/aws-samples/aws-lambda-ddns-function) from that blog is linked in as a submodule under [aws-lambda-ddns-function](aws-lambda-ddns-function/).

This code is intended to be deployed per region, and will handle all of the DNS registration for EC2
instances deployed, assumign specific tags exist.

It will create:

- DynamoDB Table (inf-dynamic-route53-{region})
- IAM Roles
- Lambda
- CloudWatch Events
- CloudWatch Log

## Operation

See the the [blog](#blog) for full details on how it works. The short version is:

- Cloudwatch event on instance (starting, started, terminated)
- Run lambda
- On startup
- Get instance details (id, region, ipv4, ipv6)
- Determine zone from tag(s)
- Find zone
- Add records if found
- Log action
- Record in DDB name and details
- On terminate
- Get instance detail (id)
- search DDB table for id
- Remove records, if in table
- Log action


## Tags

A number of tags will be used to affect behavior of the DNS entries.

### Tag: Name

The `Name` tag is the primary tag that will be used to determine the DNS name to create. It is expected to be a unique FQDN. If no Name
tag is provided, the hostname portion of the name will be constructed from the IP address:

* IPv4
* ip address: A.B.C.D
* hostname: ip-A-B-C-D
* IPv6 (TBD)

The domain portion of the `Name` tag must exist within Route53 in order for any records to be created.

### Tag: boc:dns:zone

The `boc:dns:zone` tag will be used in case we need to force a specific domain name on a host, either because it cannot obtain
the proper zone (domain) from the `Name` tag, of that a custom per-instance `Name` tag cannot be created. This latter condition
occurs for systems which work from a launch template, such as EMR or EKS.

### Tag: boc:dns:alias

The `boc:dns:alias` tag is used to create an alternate DNS name (CNAME), pointed to the primary name. It is an FQDN, and the same conditions
apply as with [Name](#tag--name).

# Links

## github aws-lambda-ddns-function
* https://github.com/aws-samples/aws-lambda-ddns-function
## Blog
* https://aws.amazon.com/blogs/security/simplify-dns-management-in-a-multiaccount-environment-with-route-53-resolver/


# Repository Setup Details

* One time

```script
git submodule add https://github.com/aws-samples/aws-lambda-ddns-function aws-lambda-ddns-function
git commit -m'add submodule' aws-lambda-ddns-function
```

* After first clone

```script
git submodule update --init
```

* Pull new stuff from submoduule

```script
git submodule foreach git pull origin master
```
<!-- BEGIN_TF_DOCS -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 3.66.0 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 3.66.0 |

## Modules

No modules.

## Resources

| Name | Type |
|------|------|
| [aws_cloudwatch_event_rule.ec2_rule](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource |
| [aws_cloudwatch_event_target.ec2_target](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource |
| [aws_cloudwatch_log_group.log](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
| [aws_dynamodb_table.table](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/dynamodb_table) | resource |
| [aws_iam_role.role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy_attachment.role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_lambda_alias.lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_alias) | resource |
| [aws_lambda_function.lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource |
| [aws_lambda_permission.allow_cloudwatch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource |
| [aws_arn.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/arn) | data source |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_iam_policy.lambda_policies](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy) | data source |
| [aws_iam_policy_document.lambda_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.lambda_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | 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_account_alias"></a> [account\_alias](#input\_account\_alias) | AWS Account Alias | `string` | `""` | no |
| <a name="input_account_id"></a> [account\_id](#input\_account\_id) | AWS Account ID (default will pull from current user) | `string` | `""` | no |
| <a name="input_component_tags"></a> [component\_tags](#input\_component\_tags) | Additional tags for Components (s3, kms, ddb) | `map(map(string))` | <pre>{<br> "ddb": {},<br> "kms": {},<br> "s3": {}<br>}</pre> | no |
| <a name="input_create"></a> [create](#input\_create) | Flag to indicate whether to create the resources or not (default: true) | `bool` | `true` | no |
| <a name="input_dynamodb_table_name"></a> [dynamodb\_table\_name](#input\_dynamodb\_table\_name) | Different DynamoDB table name to override default of var.name) | `string` | `null` | no |
| <a name="input_lambda_environment_variables"></a> [lambda\_environment\_variables](#input\_lambda\_environment\_variables) | Map of lambda environment variables and values | `map(string)` | <pre>{<br> "DNS_RR_TimeToLive": 60,<br> "DynamoDBName": null,<br> "SleepTime": 60,<br> "TagKeyCname": "boc:dns:cname",<br> "TagKeyHostName": "TBD",<br> "TagKeyZone": "boc:dns:zone"<br>}</pre> | no |
| <a name="input_lambda_name"></a> [lambda\_name](#input\_lambda\_name) | Different Lambda name to override default of var.name) | `string` | `null` | no |
| <a name="input_name"></a> [name](#input\_name) | Name to use within all the created resources (default: inf-dynamic-route53) | `string` | `"inf-dynamic-route53"` | no |
| <a name="input_override_prefixes"></a> [override\_prefixes](#input\_override\_prefixes) | Override built-in prefixes by component. This should be used primarily for common infrastructure things | `map(string)` | `{}` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | AWS Tags to apply to appropriate resources | `map(string)` | `{}` | no |

## Outputs

No outputs.
<!-- END_TF_DOCS -->
63 changes: 63 additions & 0 deletions cloudwatch.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
locals {
cloudwatch_name = format("/aws/lambda/%v", local.lambda_name)
cloudwatch_event_pattern = {
"source" = ["aws.ec2"]
"detail-type" = ["EC2 Instance State-change Notification"]
"detail" = {
"state" = ["running", "shutting-down", "stopped"]
}
}
}

resource "aws_cloudwatch_log_group" "log" {
count = var.create ? 1 : 0
name = local.cloudwatch_name
# kms_key_id = var.kms_key_arn
retention_in_days = lookup(local._defaults["cloudwatch"], "retention_in_days", 7)

tags = merge(
local.base_tags,
var.tags,
map("Name", local.name),
)
}

# aws events put-targets --rule ec2_lambda_ddns_rule --targets Id=id123456789012,Arn=<enter-your-lambda-function-arn-here>

resource "aws_cloudwatch_event_rule" "ec2_rule" {
count = var.create ? 1 : 0
name = local.name
description = "Capture EC2 Events to hande dynamic Route53 registration"
event_pattern = jsonencode(local.cloudwatch_event_pattern)

tags = merge(
local.base_tags,
var.tags,
map("Name", local.name),
)
}

resource "aws_cloudwatch_event_target" "ec2_target" {
count = var.create ? 1 : 0
# target_id = local.name
target_id = var.create ? aws_lambda_function.lambda[0].function_name : null
arn = var.create ? aws_lambda_function.lambda[0].arn : null
rule = var.create ? aws_cloudwatch_event_rule.ec2_rule[0].name : null
# propagate_tags = true

# tags = merge(
# local.base_tags,
# var.tags,
# map("Name", local.name),
# )
}

resource "aws_lambda_permission" "allow_cloudwatch" {
count = var.create ? 1 : 0
statement_id = "AllowExecutionFromCloudWatch"
action = "lambda:InvokeFunction"
function_name = var.create ? aws_lambda_function.lambda[0].function_name : null
principal = "events.amazonaws.com"
source_arn = var.create ? aws_cloudwatch_event_rule.ec2_rule[0].arn : null
# qualifier = var.create ? aws_lambda_alias.lambda[0].name : null
}
Loading