diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index bf844ab0..9b58b438 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -22,6 +22,7 @@ Please note we have a code of conduct, please follow it in all your interactions - [Resource names](#resource-names) - [Variable names](#variable-names) - [Output names](#output-names) + - [Resources order in .tf files](#resources-order-in-tf-files) - [Names of terraform files, directories, and modules](#names-of-terraform-files-directories-and-modules) - [General configuration files](#general-configuration-files) - [Specific configuration files](#specific-configuration-files) @@ -292,13 +293,21 @@ resource "aws_route_table_association" "intra" { - If the return value is a list, it must have a plural name - Use description for outputs +#### Resources order in .tf files + +Resources in `.tf` files should be described in the following order: +1. locals +2. data +3. modules +4. resources + ### Names of terraform files, directories, and modules #### General configuration files Each terraform module and configuration contains a set of general files ending in `.tf`: -- `main.tf` - contains terraform settings if it is the top layer; or the main working code if it is a module +- `main.tf` - contains terraform settings and resources that can't be somehow grouped if it is the top layer; or the main working code if it is a module - `variables.tf` - module input values - `outputs.tf` - module output values @@ -308,27 +317,27 @@ Besides these, there may be: - `providers.tf` - contains settings from terraform providers, e.g. `aws`, `kubernetes`, etc - `iam.tf` - IAM configurations of policies, roles, etc -This is not a full list; each configuration, module, or layer may need additional files and manifests. The objective is to name them as succinctly and closer in meaning to the content as possible. Do not use prefixes. +This is not a full list; each configuration, module, or layer may need additional files and manifests. The objective is to name them as succinctly and closer in meaning to the content as possible. Do not use prefixes (for files inside modules). > Terraform itself doesn't care how many files you create. It collects all layer and module manifests into one object, builds dependencies, and executes. #### Specific configuration files -These configuration files and manifests include the following: `data "template_file"` or `templatefile ()` template resources, a logical resource group placed in a separate `.tf` file, one or more deployments to k8s using `resource "helm_release"`, module initialization, aws resources that do not require a separate module, etc. +These configuration files and manifests include a logical resource group placed in a separate `.tf` file. > It should be noted that since some kind of a logical group of resources is being, why not move it all into a separate module. But it turned out that it is easier to manage helm releases, templates for them, and additional resources in separate `.tf` files at the root of a layer. And for many such configurations, when moving to modules, the amount of code can double + what we move to modules is usually what we are going to reuse. -Each specific `.tf` file must begin with a prefix indicating the service or provider to which the main resource or group being created belongs, e.g. `aws`. Optionally, the type of service is indicated next, e.g. `iam`. Next comes the name of the main service or resource or resource group declared inside, and after that, an explanatory suffix can optionally be added if there are several such files. All the parts of the name are separated by hyphens` +Each specific `.tf` file must begin with a prefix indicating the service or provider to which the main resource or group being created belongs, e.g. `aws`. Next comes the name of the main service or resource or resource group declared inside, and after that, an explanatory suffix can optionally be added if there are several such files. All the parts of the name are separated by hyphens` -So the formula looks like this: `provider|servicename`-[`optional resource/service type`]-`main resourcename|group-name`-[`optional suffix`].tf +So the formula looks like this: `provider|servicename`-`main resourcename|group-name`-[`optional suffix`].tf Examples: -- `aws-vpc.tf` - terraform manifest describing the creation of a single vpc +- `aws-vpc.tf` - terraform manifest describing the creation of a group resources for vpc (vpc + vpc endpoints) - `aws-vpc-stage.tf` - terraform manifest describing the creation of one of several vpc, for staging -- `eks-namespaces.tf` - group of namespaces created in the EKS cluster - `eks-external-dns.tf` - contains the description of external-dns service deployment to the EKS cluster -- `aws-ec2-pritunl.tf` - contains the initialization of the module that creates an EC2 instance in AWS with pritunl configured + +If a resource isn't related to any others (for example: `resource "aws_iam_account_password_policy" "default"`), it can be stored in the `main.tf` file. #### Modules @@ -338,24 +347,24 @@ Examples: - `eks-rbac-ci` - module for creating rbac for CI inside the EKS cluster - `aws-iam-autoscaler` - module for creating IAM policies for autoscaler -- `aws-ec2-pritunl` - module for creating pritunl ec2 instance +- `aws-pritunl` - module for creating pritunl ec2 instance ### Project structure --- -| FILE / DIRECTORY| DESCRIPTION | -| --------------- |:-------------:| -| docker/ | custom dockerfiles for examples | -| examples/ | example k8s deployments | -| helm-charts/ | directory contains custom helm charts | -| helm-charts/certificate | helm chart which creates ssl certificate for nginx ingress | +| FILE / DIRECTORY | DESCRIPTION | +| -------------------------- | :-------------------------------------------------------------: | +| docker/ | custom dockerfiles for examples | +| examples/ | example k8s deployments | +| helm-charts/ | directory contains custom helm charts | +| helm-charts/certificate | helm chart which creates ssl certificate for nginx ingress | | helm-charts/cluster-issuer | helm chart which creates cluster-issuer using cert manager cdrs | -| helm-charts/elk | umbrella chart to deploy elk stack | -| helm-charts/teamcity | helm chart which deploys teamcity agent and/or server | -|terraform/| directory contains terraform configuration files | -|terraform/layer1-aws| directory contains aws resources | -|terraform/layer2-k8s| directory contains resources deployed to k8s-EKS | -|terraform/modules| directory contains terraform modules | -|.editorconfig| | -|.gitlab-ci.yml|| -|.pre-commit-config.yaml|| +| helm-charts/elk | umbrella chart to deploy elk stack | +| helm-charts/teamcity | helm chart which deploys teamcity agent and/or server | +| terraform/ | directory contains terraform configuration files | +| terraform/layer1-aws | directory contains aws resources | +| terraform/layer2-k8s | directory contains resources deployed to k8s-EKS | +| terraform/modules | directory contains terraform modules | +| .editorconfig | | +| .gitlab-ci.yml | | +| .pre-commit-config.yaml | | diff --git a/.github/workflows/terraform-ci.yml b/.github/workflows/terraform-ci.yml index 2ddd37ba..56db82c7 100644 --- a/.github/workflows/terraform-ci.yml +++ b/.github/workflows/terraform-ci.yml @@ -1,4 +1,4 @@ -name: 'Terraform-ci' +name: "Terraform-ci" on: [push, pull_request] @@ -9,118 +9,87 @@ defaults: jobs: # Terraform validate configuration terraform-validate: - name: 'Terraform-validate' + name: "Terraform-validate" runs-on: ubuntu-latest container: image: maddevsio/terraform-utils:latest env: PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Terraform Init l1 - working-directory: ./terraform/layer1-aws - run: terraform init -backend=false - - name: Terraform Init l2 - working-directory: ./terraform/layer2-k8s - run: terraform init -backend=false - - name: Terraform Validate l1 - working-directory: ./terraform/layer1-aws - run: terraform validate -no-color . - - name: Terraform Validate l2 - working-directory: ./terraform/layer2-k8s - run: terraform validate -no-color . - - name: Upload files for l1 - uses: actions/upload-artifact@v2 - with: - name: l1 - path: ./terraform/layer1-aws/.terraform - retention-days: 1 - - name: Upload files for l2 - uses: actions/upload-artifact@v2 - with: - name: l2 - path: ./terraform/layer2-k8s/.terraform - retention-days: 1 + - name: Checkout + uses: actions/checkout@v2 + - name: Terraform Init l1 + working-directory: ./terraform/layer1-aws + run: terraform init -backend=false + - name: Terraform Init l2 + working-directory: ./terraform/layer2-k8s + run: terraform init -backend=false + - name: Terraform Validate l1 + working-directory: ./terraform/layer1-aws + run: terraform validate -no-color . + - name: Terraform Validate l2 + working-directory: ./terraform/layer2-k8s + run: terraform validate -no-color . + - name: Upload files for l1 + uses: actions/upload-artifact@v2 + with: + name: l1 + path: ./terraform/layer1-aws/.terraform + retention-days: 1 # Checks that all Terraform configuration files format terraform-format: - name: 'Terraform-format' + name: "Terraform-format" runs-on: ubuntu-latest container: image: maddevsio/terraform-utils:latest env: PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Terraform Format - run: terraform fmt -recursive -write=false -check . - working-directory: ./terraform + - name: Checkout + uses: actions/checkout@v2 + - name: Terraform Format + run: terraform fmt -recursive -write=false -check . + working-directory: ./terraform # Checks that all Terraform configuration files tflint terraform-tflint: - name: 'Terraform-tflint' + name: "Terraform-tflint" runs-on: ubuntu-latest container: image: maddevsio/terraform-utils:latest env: PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Terraform tflint l1 - working-directory: ./terraform/layer1-aws - run: tflint --no-color - - name: Terraform tflint l2 - working-directory: ./terraform/layer2-k8s - run: tflint --no-color + - name: Checkout + uses: actions/checkout@v2 + - name: Terraform tflint l1 + working-directory: ./terraform/layer1-aws + run: tflint --no-color + - name: Terraform tflint l2 + working-directory: ./terraform/layer2-k8s + run: tflint --no-color terraform-tfsec-l1: - name: 'Terraform-tfsec-l1' + name: "Terraform-tfsec-l1" needs: terraform-validate runs-on: ubuntu-latest container: image: tfsec/tfsec options: --user root steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Download init for l1 - uses: actions/download-artifact@v2 - with: - name: l1 - path: ./terraform/layer1-aws/.terraform - - name: tfsec l1 - working-directory: ./terraform - run: tfsec layer1-aws - - uses: geekyeggo/delete-artifact@v1 - with: - name: l1 - failOnError: false - if: ${{ always() }} - - terraform-tfsec-l2: - name: 'Terraform-tfsec-l2' - needs: terraform-validate - runs-on: ubuntu-latest - container: - image: tfsec/tfsec - options: --user root - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Download init for l2 - uses: actions/download-artifact@v2 - with: - name: l2 - path: ./terraform/layer2-k8s/.terraform - - name: Terraform tfsec l2 - working-directory: ./terraform - run: tfsec layer2-k8s - - uses: geekyeggo/delete-artifact@v1 - with: - name: l2 - failOnError: false - if: ${{ always() }} - + - name: Checkout + uses: actions/checkout@v2 + - name: Download init for l1 + uses: actions/download-artifact@v2 + with: + name: l1 + path: ./terraform/layer1-aws/.terraform + - name: tfsec l1 + working-directory: ./terraform + run: tfsec layer1-aws + - uses: geekyeggo/delete-artifact@v1 + with: + name: l1 + failOnError: false + if: ${{ always() }} diff --git a/terraform/layer1-aws/README.md b/terraform/layer1-aws/README.md index 03d3050f..11a9f2ea 100644 --- a/terraform/layer1-aws/README.md +++ b/terraform/layer1-aws/README.md @@ -22,7 +22,7 @@ | [acm](#module\_acm) | terraform-aws-modules/acm/aws | 3.3.0 | | [eks](#module\_eks) | terraform-aws-modules/eks/aws | 18.9.0 | | [eventbridge](#module\_eventbridge) | terraform-aws-modules/eventbridge/aws | 1.14.0 | -| [pritunl](#module\_pritunl) | ../modules/aws-ec2-pritunl | n/a | +| [pritunl](#module\_pritunl) | ../modules/aws-pritunl | n/a | | [r53\_zone](#module\_r53\_zone) | terraform-aws-modules/route53/aws//modules/zones | 2.5.0 | | [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | 3.12.0 | | [vpc\_cni\_irsa](#module\_vpc\_cni\_irsa) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | 4.14.0 | @@ -33,7 +33,7 @@ | Name | Type | |------|------| | [aws_cloudtrail.main](https://registry.terraform.io/providers/aws/4.10.0/docs/resources/cloudtrail) | resource | -| [aws_ebs_encryption_by_default.this](https://registry.terraform.io/providers/aws/4.10.0/docs/resources/ebs_encryption_by_default) | resource | +| [aws_ebs_encryption_by_default.default](https://registry.terraform.io/providers/aws/4.10.0/docs/resources/ebs_encryption_by_default) | resource | | [aws_iam_account_password_policy.default](https://registry.terraform.io/providers/aws/4.10.0/docs/resources/iam_account_password_policy) | resource | | [aws_kms_key.eks](https://registry.terraform.io/providers/aws/4.10.0/docs/resources/kms_key) | resource | | [aws_s3_bucket.cloudtrail](https://registry.terraform.io/providers/aws/4.10.0/docs/resources/s3_bucket) | resource | @@ -60,7 +60,7 @@ |------|-------------|------|---------|:--------:| | [allowed\_account\_ids](#input\_allowed\_account\_ids) | List of allowed AWS account IDs | `list` | `[]` | no | | [allowed\_ips](#input\_allowed\_ips) | IP addresses allowed to connect to private resources | `list(any)` | `[]` | no | -| [aws\_account\_password\_policy](#input\_aws\_account\_password\_policy) | n/a | `any` |
{
"allow_users_to_change_password": true,
"create": true,
"hard_expiry": true,
"max_password_age": "90",
"minimum_password_length": "14",
"password_reuse_prevention": "10",
"require_lowercase_characters": true,
"require_numbers": true,
"require_symbols": true,
"require_uppercase_characters": true
}
| no | +| [aws\_account\_password\_policy](#input\_aws\_account\_password\_policy) | n/a | `any` |
{
"allow_users_to_change_password": true,
"create": true,
"hard_expiry": false,
"max_password_age": "90",
"minimum_password_length": "14",
"password_reuse_prevention": "10",
"require_lowercase_characters": true,
"require_numbers": true,
"require_symbols": true,
"require_uppercase_characters": true
}
| no | | [aws\_cis\_benchmark\_alerts](#input\_aws\_cis\_benchmark\_alerts) | AWS CIS Benchmark alerts configuration | `any` |
{
"email": "demo@example.com",
"enabled": "false",
"rules": {
"aws_config_changes_enabled": true,
"cloudtrail_configuration_changes_enabled": true,
"console_login_failed_enabled": true,
"consolelogin_without_mfa_enabled": true,
"iam_policy_changes_enabled": true,
"kms_cmk_delete_or_disable_enabled": true,
"nacl_changes_enabled": true,
"network_gateway_changes_enabled": true,
"organization_changes_enabled": true,
"parameter_store_actions_enabled": true,
"route_table_changes_enabled": true,
"s3_bucket_policy_changes_enabled": true,
"secrets_manager_actions_enabled": true,
"security_group_changes_enabled": true,
"unauthorized_api_calls_enabled": true,
"usage_of_root_account_enabled": true,
"vpc_changes_enabled": true
}
}
| no | | [az\_count](#input\_az\_count) | Count of avaiablity zones, min 2 | `number` | `3` | no | | [cidr](#input\_cidr) | Default CIDR block for VPC | `string` | `"10.0.0.0/16"` | no | diff --git a/terraform/layer1-aws/aws-acm.tf b/terraform/layer1-aws/aws-acm.tf index 97c0e609..2960b930 100644 --- a/terraform/layer1-aws/aws-acm.tf +++ b/terraform/layer1-aws/aws-acm.tf @@ -1,3 +1,13 @@ +data "aws_acm_certificate" "main" { + count = var.create_acm_certificate ? 0 : 1 + + domain = var.domain_name + statuses = [ + "ISSUED", + "PENDING_VALIDATION"] + most_recent = true +} + module "acm" { source = "terraform-aws-modules/acm/aws" version = "3.3.0" @@ -11,13 +21,3 @@ module "acm" { tags = local.tags } - -data "aws_acm_certificate" "main" { - count = var.create_acm_certificate ? 0 : 1 - - domain = var.domain_name - statuses = [ - "ISSUED", - "PENDING_VALIDATION"] - most_recent = true -} diff --git a/terraform/layer1-aws/aws-eventbridge.tf b/terraform/layer1-aws/aws-cis-benchmark-alerts.tf similarity index 89% rename from terraform/layer1-aws/aws-eventbridge.tf rename to terraform/layer1-aws/aws-cis-benchmark-alerts.tf index 71edd468..4d68159e 100644 --- a/terraform/layer1-aws/aws-eventbridge.tf +++ b/terraform/layer1-aws/aws-cis-benchmark-alerts.tf @@ -456,3 +456,63 @@ module "eventbridge" { tags = local.tags } +#tfsec:ignore:aws-sns-enable-topic-encryption +resource "aws_sns_topic" "security_alerts" { + count = var.aws_cis_benchmark_alerts.enabled ? 1 : 0 + + name = "${local.name}-security-alerts" + + tags = local.tags +} + +resource "aws_sns_topic_subscription" "security_alerts" { + count = var.aws_cis_benchmark_alerts.enabled ? 1 : 0 + + topic_arn = aws_sns_topic.security_alerts[count.index].arn + protocol = "email" + endpoint = var.aws_cis_benchmark_alerts.email +} + +resource "aws_sns_topic_policy" "security_alerts" { + count = var.aws_cis_benchmark_alerts.enabled ? 1 : 0 + + arn = aws_sns_topic.security_alerts[count.index].arn + policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Sid" : "Allow_Publish_Events", + "Effect" : "Allow", + "Principal" : { "Service" : "events.amazonaws.com" }, + "Action" : "sns:Publish", + "Resource" : [ + aws_sns_topic.security_alerts[count.index].arn + ] + }, + { + "Sid" : "__default_statement_ID", + "Effect" : "Allow", + "Principal" : { "AWS" : "*" }, + "Action" : [ + "sns:GetTopicAttributes", + "sns:SetTopicAttributes", + "sns:AddPermission", + "sns:RemovePermission", + "sns:DeleteTopic", + "sns:Subscribe", + "sns:ListSubscriptionsByTopic", + "sns:Publish", + "sns:Receive" + ] + "Resource" : [ + aws_sns_topic.security_alerts[count.index].arn + ] + "Condition" : { + "StringEquals" : { + "AWS:SourceOwner" : data.aws_caller_identity.current.account_id + } + } + } + ] + }) +} diff --git a/terraform/layer1-aws/aws-cloudtrail.tf b/terraform/layer1-aws/aws-cloudtrail.tf index d8d06212..755154f5 100644 --- a/terraform/layer1-aws/aws-cloudtrail.tf +++ b/terraform/layer1-aws/aws-cloudtrail.tf @@ -8,3 +8,72 @@ resource "aws_cloudtrail" "main" { tags = local.tags } + +resource "aws_s3_bucket" "cloudtrail" { + bucket = "${local.name}-aws-cloudtrail-logs" + + tags = local.tags +} + +resource "aws_s3_bucket_acl" "cloudtrail" { + bucket = aws_s3_bucket.cloudtrail.id + acl = "private" +} + +resource "aws_s3_bucket_lifecycle_configuration" "cloudtrail" { + bucket = aws_s3_bucket.cloudtrail.id + + rule { + id = "remove_old_files" + status = "Enabled" + + abort_incomplete_multipart_upload { + days_after_initiation = 2 + } + expiration { + days = var.cloudtrail_logs_s3_expiration_days + } + } +} + +resource "aws_s3_bucket_public_access_block" "cloudtrail" { + bucket = aws_s3_bucket.cloudtrail.id + restrict_public_buckets = true + block_public_acls = true + block_public_policy = true + ignore_public_acls = true +} + +resource "aws_s3_bucket_policy" "cloudtrail" { + bucket = aws_s3_bucket.cloudtrail.id + + policy = jsonencode( + { + "Version" : "2012-10-17", + "Statement" : [ + { + "Sid" : "AWSCloudTrailAclCheck", + "Effect" : "Allow", + "Principal" : { + "Service" : "cloudtrail.amazonaws.com" + }, + "Action" : "s3:GetBucketAcl", + "Resource" : aws_s3_bucket.cloudtrail.arn + }, + { + "Sid" : "AWSCloudTrailWrite", + "Effect" : "Allow", + "Principal" : { + "Service" : "cloudtrail.amazonaws.com" + }, + "Action" : "s3:PutObject", + "Resource" : "${aws_s3_bucket.cloudtrail.arn}/*", + "Condition" : { + "StringEquals" : { + "s3:x-amz-acl" : "bucket-owner-full-control" + } + } + } + ] + }) +} diff --git a/terraform/layer1-aws/aws-ebs.tf b/terraform/layer1-aws/aws-ebs.tf deleted file mode 100644 index 1a20ec8f..00000000 --- a/terraform/layer1-aws/aws-ebs.tf +++ /dev/null @@ -1,3 +0,0 @@ -resource "aws_ebs_encryption_by_default" "this" { - enabled = true -} diff --git a/terraform/layer1-aws/aws-eks-auth.tf b/terraform/layer1-aws/aws-eks-auth.tf deleted file mode 100644 index a69367af..00000000 --- a/terraform/layer1-aws/aws-eks-auth.tf +++ /dev/null @@ -1,21 +0,0 @@ -locals { - eks_map_roles = [ - { - rolearn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/administrator" - username = "administrator" - groups = ["system:masters"] - } - ] - eks_map_users = [] - - aws_auth_configmap_yaml = <<-CONTENT - ${chomp(module.eks.aws_auth_configmap_yaml)} - ${indent(4, yamlencode(local.eks_map_roles))} - mapUsers: | - ${indent(4, yamlencode(local.eks_map_users))} - CONTENT -} - -resource "kubectl_manifest" "aws_auth_configmap" { - yaml_body = local.aws_auth_configmap_yaml -} diff --git a/terraform/layer1-aws/aws-eks.tf b/terraform/layer1-aws/aws-eks.tf index c7beb533..88609149 100644 --- a/terraform/layer1-aws/aws-eks.tf +++ b/terraform/layer1-aws/aws-eks.tf @@ -5,6 +5,22 @@ locals { } eks_addon_vpc_cni = merge(var.eks_addons.vpc-cni, { service_account_role_arn = module.vpc_cni_irsa.iam_role_arn }) eks_addons = merge(var.eks_addons, { vpc-cni = local.eks_addon_vpc_cni }) + + eks_map_roles = [ + { + rolearn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/administrator" + username = "administrator" + groups = ["system:masters"] + } + ] + eks_map_users = [] + + aws_auth_configmap_yaml = <<-CONTENT + ${chomp(module.eks.aws_auth_configmap_yaml)} + ${indent(4, yamlencode(local.eks_map_roles))} + mapUsers: | + ${indent(4, yamlencode(local.eks_map_users))} + CONTENT } data "aws_ami" "eks_default_bottlerocket" { @@ -212,3 +228,30 @@ module "eks" { } } + +module "vpc_cni_irsa" { + source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" + version = "4.14.0" + + role_name = "${local.name}-vpc-cni" + attach_vpc_cni_policy = true + vpc_cni_enable_ipv4 = true + + oidc_providers = { + main = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["kube-system:aws-node"] + } + } + + tags = local.tags +} + +resource "aws_kms_key" "eks" { + count = var.eks_cluster_encryption_config_enable ? 1 : 0 + description = "EKS Secret Encryption Key" +} + +resource "kubectl_manifest" "aws_auth_configmap" { + yaml_body = local.aws_auth_configmap_yaml +} diff --git a/terraform/layer1-aws/aws-iam.tf b/terraform/layer1-aws/aws-iam.tf deleted file mode 100644 index 590150a6..00000000 --- a/terraform/layer1-aws/aws-iam.tf +++ /dev/null @@ -1,30 +0,0 @@ -resource "aws_iam_account_password_policy" "default" { - count = var.aws_account_password_policy.create ? 1 : 0 - - minimum_password_length = var.aws_account_password_policy.minimum_password_length - password_reuse_prevention = var.aws_account_password_policy.password_reuse_prevention - require_lowercase_characters = var.aws_account_password_policy.require_lowercase_characters - require_numbers = var.aws_account_password_policy.require_numbers - require_uppercase_characters = var.aws_account_password_policy.require_uppercase_characters - require_symbols = var.aws_account_password_policy.require_symbols - allow_users_to_change_password = var.aws_account_password_policy.allow_users_to_change_password - max_password_age = var.aws_account_password_policy.max_password_age -} - -module "vpc_cni_irsa" { - source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" - version = "4.14.0" - - role_name = "${local.name}-vpc-cni" - attach_vpc_cni_policy = true - vpc_cni_enable_ipv4 = true - - oidc_providers = { - main = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["kube-system:aws-node"] - } - } - - tags = local.tags -} diff --git a/terraform/layer1-aws/aws-kms.tf b/terraform/layer1-aws/aws-kms.tf deleted file mode 100644 index aed51b97..00000000 --- a/terraform/layer1-aws/aws-kms.tf +++ /dev/null @@ -1,4 +0,0 @@ -resource "aws_kms_key" "eks" { - count = var.eks_cluster_encryption_config_enable ? 1 : 0 - description = "EKS Secret Encryption Key" -} diff --git a/terraform/layer1-aws/aws-ec2-pritunl.tf b/terraform/layer1-aws/aws-pritunl.tf similarity index 94% rename from terraform/layer1-aws/aws-ec2-pritunl.tf rename to terraform/layer1-aws/aws-pritunl.tf index bc28466b..49564147 100644 --- a/terraform/layer1-aws/aws-ec2-pritunl.tf +++ b/terraform/layer1-aws/aws-pritunl.tf @@ -2,7 +2,7 @@ module "pritunl" { count = var.pritunl_vpn_server_enable ? 1 : 0 - source = "../modules/aws-ec2-pritunl" + source = "../modules/aws-pritunl" environment = local.env vpc_id = module.vpc.vpc_id public_subnets = module.vpc.public_subnets diff --git a/terraform/layer1-aws/aws-r53.tf b/terraform/layer1-aws/aws-r53.tf index fb04064b..25abed04 100644 --- a/terraform/layer1-aws/aws-r53.tf +++ b/terraform/layer1-aws/aws-r53.tf @@ -1,3 +1,10 @@ +data "aws_route53_zone" "main" { + count = var.create_r53_zone && var.zone_id == null ? 0 : 1 + + name = "${var.domain_name}." + private_zone = false +} + module "r53_zone" { source = "terraform-aws-modules/route53/aws//modules/zones" version = "2.5.0" @@ -11,10 +18,3 @@ module "r53_zone" { } } } - -data "aws_route53_zone" "main" { - count = var.create_r53_zone && var.zone_id == null ? 0 : 1 - - name = "${var.domain_name}." - private_zone = false -} diff --git a/terraform/layer1-aws/aws-s3.tf b/terraform/layer1-aws/aws-s3.tf deleted file mode 100644 index d8d8c047..00000000 --- a/terraform/layer1-aws/aws-s3.tf +++ /dev/null @@ -1,72 +0,0 @@ -################## -# Cloudtrail -################## -resource "aws_s3_bucket" "cloudtrail" { - bucket = "${local.name}-aws-cloudtrail-logs" - - tags = local.tags -} - -resource "aws_s3_bucket_acl" "cloudtrail" { - bucket = aws_s3_bucket.cloudtrail.id - acl = "private" -} - -resource "aws_s3_bucket_lifecycle_configuration" "cloudtrail" { - bucket = aws_s3_bucket.cloudtrail.id - - rule { - id = "remove_old_files" - status = "Enabled" - - abort_incomplete_multipart_upload { - days_after_initiation = 2 - } - expiration { - days = var.cloudtrail_logs_s3_expiration_days - } - } -} - -resource "aws_s3_bucket_public_access_block" "cloudtrail" { - bucket = aws_s3_bucket.cloudtrail.id - restrict_public_buckets = true - block_public_acls = true - block_public_policy = true - ignore_public_acls = true -} - -resource "aws_s3_bucket_policy" "cloudtrail" { - bucket = aws_s3_bucket.cloudtrail.id - - policy = jsonencode( - { - "Version" : "2012-10-17", - "Statement" : [ - { - "Sid" : "AWSCloudTrailAclCheck", - "Effect" : "Allow", - "Principal" : { - "Service" : "cloudtrail.amazonaws.com" - }, - "Action" : "s3:GetBucketAcl", - "Resource" : aws_s3_bucket.cloudtrail.arn - }, - { - "Sid" : "AWSCloudTrailWrite", - "Effect" : "Allow", - "Principal" : { - "Service" : "cloudtrail.amazonaws.com" - }, - "Action" : "s3:PutObject", - "Resource" : "${aws_s3_bucket.cloudtrail.arn}/*", - "Condition" : { - "StringEquals" : { - "s3:x-amz-acl" : "bucket-owner-full-control" - } - } - } - ] - }) -} -#################### diff --git a/terraform/layer1-aws/aws-sns.tf b/terraform/layer1-aws/aws-sns.tf deleted file mode 100644 index d21ca825..00000000 --- a/terraform/layer1-aws/aws-sns.tf +++ /dev/null @@ -1,60 +0,0 @@ -#tfsec:ignore:aws-sns-enable-topic-encryption -resource "aws_sns_topic" "security_alerts" { - count = var.aws_cis_benchmark_alerts.enabled ? 1 : 0 - - name = "${local.name}-security-alerts" - - tags = local.tags -} - -resource "aws_sns_topic_subscription" "security_alerts" { - count = var.aws_cis_benchmark_alerts.enabled ? 1 : 0 - - topic_arn = aws_sns_topic.security_alerts[count.index].arn - protocol = "email" - endpoint = var.aws_cis_benchmark_alerts.email -} - -resource "aws_sns_topic_policy" "security_alerts" { - count = var.aws_cis_benchmark_alerts.enabled ? 1 : 0 - - arn = aws_sns_topic.security_alerts[count.index].arn - policy = jsonencode({ - "Version" : "2012-10-17", - "Statement" : [ - { - "Sid" : "Allow_Publish_Events", - "Effect" : "Allow", - "Principal" : { "Service" : "events.amazonaws.com" }, - "Action" : "sns:Publish", - "Resource" : [ - aws_sns_topic.security_alerts[count.index].arn - ] - }, - { - "Sid" : "__default_statement_ID", - "Effect" : "Allow", - "Principal" : { "AWS" : "*" }, - "Action" : [ - "sns:GetTopicAttributes", - "sns:SetTopicAttributes", - "sns:AddPermission", - "sns:RemovePermission", - "sns:DeleteTopic", - "sns:Subscribe", - "sns:ListSubscriptionsByTopic", - "sns:Publish", - "sns:Receive" - ] - "Resource" : [ - aws_sns_topic.security_alerts[count.index].arn - ] - "Condition" : { - "StringEquals" : { - "AWS:SourceOwner" : data.aws_caller_identity.current.account_id - } - } - } - ] - }) -} diff --git a/terraform/layer1-aws/aws-vpc-endpoints.tf b/terraform/layer1-aws/aws-vpc-endpoints.tf deleted file mode 100644 index ccc1f120..00000000 --- a/terraform/layer1-aws/aws-vpc-endpoints.tf +++ /dev/null @@ -1,30 +0,0 @@ -# https://github.com/terraform-aws-modules/terraform-aws-vpc/blob/master/examples/complete-vpc/main.tf#L82 - -data "aws_security_group" "default" { - name = "default" - vpc_id = module.vpc.vpc_id -} - -module "vpc_gateway_endpoints" { - source = "terraform-aws-modules/vpc/aws//modules/vpc-endpoints" - version = "3.12.0" - - vpc_id = module.vpc.vpc_id - - endpoints = { - s3 = { - service = "s3" - service_type = "Gateway" - route_table_ids = flatten([ - module.vpc.intra_route_table_ids, - module.vpc.private_route_table_ids, - module.vpc.public_route_table_ids - ]) - tags = { - Name = "${local.name}-s3" - } - } - } - - tags = local.tags -} diff --git a/terraform/layer1-aws/aws-vpc.tf b/terraform/layer1-aws/aws-vpc.tf index e5f1d358..077ad44a 100644 --- a/terraform/layer1-aws/aws-vpc.tf +++ b/terraform/layer1-aws/aws-vpc.tf @@ -7,6 +7,12 @@ locals { azs = data.aws_availability_zones.available.names } +# https://github.com/terraform-aws-modules/terraform-aws-vpc/blob/master/examples/complete-vpc/main.tf#L82 +data "aws_security_group" "default" { + name = "default" + vpc_id = module.vpc.vpc_id +} + module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "3.12.0" @@ -78,3 +84,27 @@ module "vpc" { destination = "intra" } } + +module "vpc_gateway_endpoints" { + source = "terraform-aws-modules/vpc/aws//modules/vpc-endpoints" + version = "3.12.0" + + vpc_id = module.vpc.vpc_id + + endpoints = { + s3 = { + service = "s3" + service_type = "Gateway" + route_table_ids = flatten([ + module.vpc.intra_route_table_ids, + module.vpc.private_route_table_ids, + module.vpc.public_route_table_ids + ]) + tags = { + Name = "${local.name}-s3" + } + } + } + + tags = local.tags +} diff --git a/terraform/layer1-aws/main.tf b/terraform/layer1-aws/main.tf index 0743c3c2..1e104d36 100644 --- a/terraform/layer1-aws/main.tf +++ b/terraform/layer1-aws/main.tf @@ -20,3 +20,20 @@ terraform { data "aws_availability_zones" "available" {} data "aws_caller_identity" "current" {} + +resource "aws_ebs_encryption_by_default" "default" { + enabled = true +} + +resource "aws_iam_account_password_policy" "default" { + count = var.aws_account_password_policy.create ? 1 : 0 + + minimum_password_length = var.aws_account_password_policy.minimum_password_length + password_reuse_prevention = var.aws_account_password_policy.password_reuse_prevention + require_lowercase_characters = var.aws_account_password_policy.require_lowercase_characters + require_numbers = var.aws_account_password_policy.require_numbers + require_uppercase_characters = var.aws_account_password_policy.require_uppercase_characters + require_symbols = var.aws_account_password_policy.require_symbols + allow_users_to_change_password = var.aws_account_password_policy.allow_users_to_change_password + max_password_age = var.aws_account_password_policy.max_password_age +} diff --git a/terraform/layer2-k8s/README.md b/terraform/layer2-k8s/README.md index 652af543..942c672a 100644 --- a/terraform/layer2-k8s/README.md +++ b/terraform/layer2-k8s/README.md @@ -34,25 +34,25 @@ | [aws\_iam\_gitlab\_runner](#module\_aws\_iam\_gitlab\_runner) | ../modules/aws-iam-eks-trusted | n/a | | [aws\_iam\_kube\_prometheus\_stack\_grafana](#module\_aws\_iam\_kube\_prometheus\_stack\_grafana) | ../modules/aws-iam-eks-trusted | n/a | | [aws\_iam\_victoria\_metrics\_k8s\_stack\_grafana](#module\_aws\_iam\_victoria\_metrics\_k8s\_stack\_grafana) | ../modules/aws-iam-eks-trusted | n/a | -| [aws\_load\_balancer\_controller\_namespace](#module\_aws\_load\_balancer\_controller\_namespace) | ../modules/kubernetes-namespace | n/a | -| [aws\_node\_termination\_handler\_namespace](#module\_aws\_node\_termination\_handler\_namespace) | ../modules/kubernetes-namespace | n/a | -| [certmanager\_namespace](#module\_certmanager\_namespace) | ../modules/kubernetes-namespace | n/a | -| [cluster\_autoscaler\_namespace](#module\_cluster\_autoscaler\_namespace) | ../modules/kubernetes-namespace | n/a | +| [aws\_load\_balancer\_controller\_namespace](#module\_aws\_load\_balancer\_controller\_namespace) | ../modules/eks-kubernetes-namespace | n/a | +| [aws\_node\_termination\_handler\_namespace](#module\_aws\_node\_termination\_handler\_namespace) | ../modules/eks-kubernetes-namespace | n/a | +| [certmanager\_namespace](#module\_certmanager\_namespace) | ../modules/eks-kubernetes-namespace | n/a | +| [cluster\_autoscaler\_namespace](#module\_cluster\_autoscaler\_namespace) | ../modules/eks-kubernetes-namespace | n/a | | [elastic\_tls](#module\_elastic\_tls) | ../modules/self-signed-certificate | n/a | -| [elk\_namespace](#module\_elk\_namespace) | ../modules/kubernetes-namespace | n/a | -| [external\_dns\_namespace](#module\_external\_dns\_namespace) | ../modules/kubernetes-namespace | n/a | -| [external\_secrets\_namespace](#module\_external\_secrets\_namespace) | ../modules/kubernetes-namespace | n/a | -| [fargate\_namespace](#module\_fargate\_namespace) | ../modules/kubernetes-namespace | n/a | -| [gitlab\_runner\_namespace](#module\_gitlab\_runner\_namespace) | ../modules/kubernetes-namespace | n/a | -| [ingress\_nginx\_namespace](#module\_ingress\_nginx\_namespace) | ../modules/kubernetes-namespace | n/a | -| [istio\_system\_namespace](#module\_istio\_system\_namespace) | ../modules/kubernetes-namespace | n/a | -| [keda\_namespace](#module\_keda\_namespace) | ../modules/kubernetes-namespace | n/a | -| [kiali\_namespace](#module\_kiali\_namespace) | ../modules/kubernetes-namespace | n/a | -| [kube\_prometheus\_stack\_namespace](#module\_kube\_prometheus\_stack\_namespace) | ../modules/kubernetes-namespace | n/a | -| [loki\_namespace](#module\_loki\_namespace) | ../modules/kubernetes-namespace | n/a | -| [reloader\_namespace](#module\_reloader\_namespace) | ../modules/kubernetes-namespace | n/a | -| [tigera\_operator\_namespace](#module\_tigera\_operator\_namespace) | ../modules/kubernetes-namespace | n/a | -| [victoria\_metrics\_k8s\_stack\_namespace](#module\_victoria\_metrics\_k8s\_stack\_namespace) | ../modules/kubernetes-namespace | n/a | +| [elk\_namespace](#module\_elk\_namespace) | ../modules/eks-kubernetes-namespace | n/a | +| [external\_dns\_namespace](#module\_external\_dns\_namespace) | ../modules/eks-kubernetes-namespace | n/a | +| [external\_secrets\_namespace](#module\_external\_secrets\_namespace) | ../modules/eks-kubernetes-namespace | n/a | +| [fargate\_namespace](#module\_fargate\_namespace) | ../modules/eks-kubernetes-namespace | n/a | +| [gitlab\_runner\_namespace](#module\_gitlab\_runner\_namespace) | ../modules/eks-kubernetes-namespace | n/a | +| [ingress\_nginx\_namespace](#module\_ingress\_nginx\_namespace) | ../modules/eks-kubernetes-namespace | n/a | +| [istio\_system\_namespace](#module\_istio\_system\_namespace) | ../modules/eks-kubernetes-namespace | n/a | +| [keda\_namespace](#module\_keda\_namespace) | ../modules/eks-kubernetes-namespace | n/a | +| [kiali\_namespace](#module\_kiali\_namespace) | ../modules/eks-kubernetes-namespace | n/a | +| [kube\_prometheus\_stack\_namespace](#module\_kube\_prometheus\_stack\_namespace) | ../modules/eks-kubernetes-namespace | n/a | +| [loki\_namespace](#module\_loki\_namespace) | ../modules/eks-kubernetes-namespace | n/a | +| [reloader\_namespace](#module\_reloader\_namespace) | ../modules/eks-kubernetes-namespace | n/a | +| [tigera\_operator\_namespace](#module\_tigera\_operator\_namespace) | ../modules/eks-kubernetes-namespace | n/a | +| [victoria\_metrics\_k8s\_stack\_namespace](#module\_victoria\_metrics\_k8s\_stack\_namespace) | ../modules/eks-kubernetes-namespace | n/a | ## Resources diff --git a/terraform/layer2-k8s/eks-aws-loadbalancer-controller.tf b/terraform/layer2-k8s/eks-aws-loadbalancer-controller.tf index db871145..37e4aac9 100644 --- a/terraform/layer2-k8s/eks-aws-loadbalancer-controller.tf +++ b/terraform/layer2-k8s/eks-aws-loadbalancer-controller.tf @@ -29,11 +29,10 @@ affinity: VALUES } -#tfsec:ignore:kubernetes-network-no-public-egress tfsec:ignore:kubernetes-network-no-public-ingress module "aws_load_balancer_controller_namespace" { count = local.aws_load_balancer_controller.enabled ? 1 : 0 - source = "../modules/kubernetes-namespace" + source = "../modules/eks-kubernetes-namespace" name = local.aws_load_balancer_controller.namespace network_policies = [ { @@ -103,7 +102,6 @@ module "aws_load_balancer_controller_namespace" { ] } -#tfsec:ignore:aws-iam-no-policy-wildcards module "aws_iam_aws_loadbalancer_controller" { count = local.aws_load_balancer_controller.enabled ? 1 : 0 diff --git a/terraform/layer2-k8s/eks-aws-node-termination-handler.tf b/terraform/layer2-k8s/eks-aws-node-termination-handler.tf index f3e216ab..54e350a7 100644 --- a/terraform/layer2-k8s/eks-aws-node-termination-handler.tf +++ b/terraform/layer2-k8s/eks-aws-node-termination-handler.tf @@ -25,11 +25,10 @@ affinity: VALUES } -#tfsec:ignore:kubernetes-network-no-public-egress tfsec:ignore:kubernetes-network-no-public-ingress module "aws_node_termination_handler_namespace" { count = local.aws_node_termination_handler.enabled ? 1 : 0 - source = "../modules/kubernetes-namespace" + source = "../modules/eks-kubernetes-namespace" name = local.aws_node_termination_handler.namespace network_policies = [ { diff --git a/terraform/layer2-k8s/eks-cert-manager.tf b/terraform/layer2-k8s/eks-cert-manager.tf index 45c1fdd9..d9f5b982 100644 --- a/terraform/layer2-k8s/eks-cert-manager.tf +++ b/terraform/layer2-k8s/eks-cert-manager.tf @@ -57,11 +57,10 @@ commonName: "${local.domain_name}" VALUES } -#tfsec:ignore:kubernetes-network-no-public-egress tfsec:ignore:kubernetes-network-no-public-ingress module "certmanager_namespace" { count = local.cert_manager.enabled ? 1 : 0 - source = "../modules/kubernetes-namespace" + source = "../modules/eks-kubernetes-namespace" name = local.cert_manager.namespace network_policies = [ { @@ -131,7 +130,6 @@ module "certmanager_namespace" { ] } -#tfsec:ignore:aws-iam-no-policy-wildcards module "aws_iam_cert_manager" { count = local.cert_manager.enabled ? 1 : 0 diff --git a/terraform/layer2-k8s/eks-cluster-autoscaler.tf b/terraform/layer2-k8s/eks-cluster-autoscaler.tf index 27650f66..4a24d37a 100644 --- a/terraform/layer2-k8s/eks-cluster-autoscaler.tf +++ b/terraform/layer2-k8s/eks-cluster-autoscaler.tf @@ -53,11 +53,10 @@ resources: VALUES } -#tfsec:ignore:kubernetes-network-no-public-egress tfsec:ignore:kubernetes-network-no-public-ingress module "cluster_autoscaler_namespace" { count = local.cluster_autoscaler.enabled ? 1 : 0 - source = "../modules/kubernetes-namespace" + source = "../modules/eks-kubernetes-namespace" name = local.cluster_autoscaler.namespace network_policies = [ { @@ -129,7 +128,6 @@ module "cluster_autoscaler_namespace" { ] } -#tfsec:ignore:aws-iam-no-policy-wildcards module "aws_iam_autoscaler" { count = local.cluster_autoscaler.enabled ? 1 : 0 diff --git a/terraform/layer2-k8s/eks-elk.tf b/terraform/layer2-k8s/eks-elk.tf index df9ef700..de52d6c4 100644 --- a/terraform/layer2-k8s/eks-elk.tf +++ b/terraform/layer2-k8s/eks-elk.tf @@ -343,8 +343,7 @@ kibana: name: elastic-credentials key: password VALUES - #tfsec:ignore:general-secrets-sensitive-in-attribute-value - elk_filebeat_values = <