diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ce0ebcb --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +insert_final_newline = true +max_line_length = 80 +trim_trailing_whitespace = true + +[*.md] +max_line_length = 0 +trim_trailing_whitespace = false diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..0f731c1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,36 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +## Describe the bug + + + +## Versions + + + +- Terraform: +- Provider: +- Module: + +## Reproduction + + + +## Expected behavior + + + +## Actual behavior + + + +## Additional context + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..951e0c8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,24 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +## Is your feature request related to a problem? Please describe. + + + +## Describe the solution you'd like + + + +## Describe alternatives you've considered + + + +## Additional context + + diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..126baa5 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,41 @@ +name: CI +on: + push: + branches: + - main + pull_request: +jobs: + pre-commit-checks: + name: Pre-commit checks + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Terraform min/max versions + id: minMax + uses: clowdhaus/terraform-min-max@v1.0.4 + - name: Pre-commit Terraform ${{ steps.minMax.outputs.maxVersion }} + uses: clowdhaus/terraform-composite-actions/pre-commit@v1.4.1 + with: + terraform-version: ${{ steps.minMax.outputs.maxVersion }} + terraform-docs-version: v0.16.0 + validate-examples: + name: Validate examples + runs-on: ubuntu-latest + defaults: + run: + shell: bash + working-directory: examples + steps: + - uses: hashicorp/setup-terraform@v1 + - name: Checkout + uses: actions/checkout@v2 + - name: Check examples + env: + EXAMPLES: simple + run: | + for EXAMPLE in ${EXAMPLES} + do + echo "Validating $EXAMPLE"... + cd $EXAMPLE && terraform init && terraform validate && cd - + done diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..e1e02b2 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,12 @@ +on: + push: + branches: + - main +name: release-please +jobs: + release-please: + runs-on: ubuntu-latest + steps: + - uses: google-github-actions/release-please-action@v3 + with: + release-type: terraform-module diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a85eada --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +.terraform.lock.hcl + +##################### +# Default gitignore # +##################### + +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log + +# Exclude all .tfvars files, which are likely to contain sentitive data, such as +# password, private keys, and other secrets. These should not be part of version +# control as they are data points which are potentially sensitive and subject +# to change depending on the environment. +# +*.tfvars + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Include override files you do wish to add to version control using negated pattern +# +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* + +# Ignore CLI configuration files +.terraformrc +terraform.rc diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..7f6597f --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,12 @@ +repos: + - repo: https://github.com/antonbabenko/pre-commit-terraform + rev: v1.76.0 + hooks: + - id: terraform_fmt + - id: terraform_docs + args: + - --args=--config=.terraform-docs.yml + - id: terraform_tflint + exclude: "test/" + args: + - --args=--config=__GIT_WORKING_DIR__/.tflint.hcl diff --git a/.terraform-docs.yml b/.terraform-docs.yml new file mode 100644 index 0000000..8369b4a --- /dev/null +++ b/.terraform-docs.yml @@ -0,0 +1,16 @@ +formatter: "markdown table" + +sections: + show: + - requirements + - providers + - inputs + - outputs + +sort: + enabled: true + by: required + +settings: + default: false + lockfile: false diff --git a/.tflint.hcl b/.tflint.hcl new file mode 100644 index 0000000..714b601 --- /dev/null +++ b/.tflint.hcl @@ -0,0 +1,53 @@ +config { + module = false + force = false + disabled_by_default = false +} + +rule "terraform_deprecated_interpolation" { + enabled = true +} + +rule "terraform_deprecated_index" { + enabled = true +} + +rule "terraform_unused_declarations" { + enabled = true +} + +rule "terraform_comment_syntax" { + enabled = true +} + +rule "terraform_documented_outputs" { + enabled = true +} + +rule "terraform_documented_variables" { + enabled = true +} + +rule "terraform_typed_variables" { + enabled = true +} + +rule "terraform_module_pinned_source" { + enabled = true +} + +rule "terraform_required_version" { + enabled = true +} + +rule "terraform_required_providers" { + enabled = true +} + +rule "terraform_standard_module_structure" { + enabled = true +} + +rule "terraform_workspace_remote" { + enabled = true +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7e51950 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Takashi Nozawa + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..18aad7e --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +# terraform-aws-parked-domain-baseline + +[![Github Actions](https://github.com/nozaq/terraform-aws-parked-domain-baseline/actions/workflows/main.yml/badge.svg)](https://github.com/nozaq/terraform-aws-parked-domain-baseline/actions/workflows/main.yml) +[![Releases](https://img.shields.io/github/v/release/nozaq/terraform-aws-parked-domain-baseline)](https://github.com/nozaq/terraform-aws-parked-domain-baseline/releases/latest) + +[Terraform Module Registry](https://registry.terraform.io/modules/nozaq/parked-domain-baseline/aws) + +A terraform module to set up DNS records to harden the parked(unused) domain using AWS Route53. + +Domains should be protected for email spoofing even if they are not intended to be actively used. +This module configures DNS records to protect such domain based on [M3AAWG Protecting Parked Domains Best Common Practices]. + +## Features + +This module creates the following DNS records. + +- Null MX record([RFC 7505]) to indicate the domain does not accept any email. +- SPF record to indicate no IP is authorized to send email on behalf of this domain. +- DMARC record to enforce receiving domains to reject any email forging this domain. +- Optionally adds `rua` tag in the DMARC record to receive aggregate feedback reports via email. +- Optionally creates Null MX and DMARC records for wildcard subdomains as well as the root domain(enabled by default). + +## Usage + +```hcl +provider "aws" { +} + +data "aws_route53_zone" "this" { + name = "example.com" +} + +module "parked_domain" { + source = "nozaq/parked-domain-baseline/aws" + + zone_id = data.aws_route53_zone.this.zone_id + ttl = 86400 # One day +} +``` + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3 | +| [aws](#requirement\_aws) | >= 4.40 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.40 | + +## Inputs + +| Name | Description | Type | Required | +|------|-------------|------|:--------:| +| [aggregate\_feedback\_email](#input\_aggregate\_feedback\_email) | The email address to which aggregate feedback is to be sent. | `string` | no | +| [include\_subdomains](#input\_include\_subdomains) | Configure all subdomains as well as the root domain. | `bool` | no | +| [ttl](#input\_ttl) | The TTL of the DNS records. | `number` | no | +| [zone\_id](#input\_zone\_id) | The DNS zone ID to add the records to. Either zone\_name or zone\_id need to be given. | `string` | no | + +## Outputs + +No outputs. + + +[M3AAWG Protecting Parked Domains Best Common Practices]: https://www.m3aawg.org/sites/default/files/m3aawg_parked_domains_bcp-2022-06.pdf +[RFC 7505]: https://datatracker.ietf.org/doc/rfc7505/ diff --git a/examples/simple/main.tf b/examples/simple/main.tf new file mode 100644 index 0000000..9af08be --- /dev/null +++ b/examples/simple/main.tf @@ -0,0 +1,24 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.40" + } + } + + required_version = ">= 1.3" +} + +provider "aws" { +} + +data "aws_route53_zone" "this" { + name = var.zone_name +} + +module "parked_domain" { + source = "../../" + + zone_id = data.aws_route53_zone.this.zone_id + ttl = 86400 # One day +} diff --git a/examples/simple/outputs.tf b/examples/simple/outputs.tf new file mode 100644 index 0000000..e69de29 diff --git a/examples/simple/variables.tf b/examples/simple/variables.tf new file mode 100644 index 0000000..5c48f82 --- /dev/null +++ b/examples/simple/variables.tf @@ -0,0 +1,4 @@ +variable "zone_name" { + description = "Hosted Zone name in which DNS records are create." + type = string +} diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..f600387 --- /dev/null +++ b/main.tf @@ -0,0 +1,58 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.40" + } + } + required_version = ">= 1.3" +} + +# Null MX record specified in RFC 7505 +# https://datatracker.ietf.org/doc/rfc7505/ +resource "aws_route53_record" "mx_root" { + zone_id = var.zone_id + type = "MX" + name = "" + records = ["0 ."] + ttl = var.ttl +} + +resource "aws_route53_record" "mx_subdomains" { + count = var.include_subdomains ? 1 : 0 + + zone_id = var.zone_id + type = "MX" + name = "*" + records = ["0 ."] + ttl = var.ttl +} + +# Ensure all SPF validations to fail for the root domain. +resource "aws_route53_record" "spf_root" { + zone_id = var.zone_id + type = "TXT" + name = "" + records = ["v=spf1 -all"] + ttl = var.ttl +} + +# Ensure all SPF validations to fail for subdomains. +resource "aws_route53_record" "spf_subdomains" { + count = var.include_subdomains ? 1 : 0 + + zone_id = var.zone_id + type = "TXT" + name = "*" + records = ["v=spf1 -all"] + ttl = var.ttl +} + +# Advise receivers to reject emails when DMARC alignment fails. +resource "aws_route53_record" "dmarc" { + zone_id = var.zone_id + type = "TXT" + name = "_dmarc" + records = [var.aggregate_feedback_email != "" ? "v=DMARC1; p=reject; rua=${var.aggregate_feedback_email}" : "v=DMARC1; p=reject;"] + ttl = var.ttl +} diff --git a/outputs.tf b/outputs.tf new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/outputs.tf @@ -0,0 +1 @@ + diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..473b6d2 --- /dev/null +++ b/variables.tf @@ -0,0 +1,23 @@ +variable "zone_id" { + description = "The DNS zone ID to add the records to. Either zone_name or zone_id need to be given." + type = string + default = "" +} + +variable "ttl" { + description = "The TTL of the DNS records." + type = number + default = 1 +} + +variable "aggregate_feedback_email" { + description = "The email address to which aggregate feedback is to be sent." + type = string + default = "" +} + +variable "include_subdomains" { + description = "Configure all subdomains as well as the root domain." + type = bool + default = true +}