diff --git a/docs/infrastructure/deployment.md b/docs/infrastructure/deployment.md index ef9d6882..b3b45a20 100644 --- a/docs/infrastructure/deployment.md +++ b/docs/infrastructure/deployment.md @@ -88,7 +88,6 @@ There are two config files needed to instantiate the rAPId service, they are `in state_bucket = "" data_bucket_name = "" log_bucket_name = "" -iam_account_alias = "" application_version = "" ui_version = "" @@ -151,40 +150,7 @@ To set up the S3 backend follow these steps: - Replace the values in `backend.hcl` with your custom values (these can be any value you would like). They will be referenced to create the Terraform state components and used going forwards as the backend config. - In the root folder run `make infra/backend`, this will initialise Terraform by creating both the state bucket and dynamodb table in AWS. -### IAM User Setup (Optional) - -This module is used to set the admin and user roles for the AWS account. It ensures a good level of security by expiring credentials quickly and ensuring that MFA is always needed to refresh them. - -> CAUTION: If you have existing AWS users and don't include them as part of the manual_users, this block will remove them! - -You will need define the desired IAM users (both new and previous manually added -ones) in `input-params.tfvars` and deploy the [iam-config](#deploying-remaining-infra-blocks) block with admin privileges. After that you can use [assume-role](#assume-role) to perform infrastructure changes. - -`infra-params.tfvars` snippet for 'user1': - -``` -set_iam_user_groups = ["users", "admins"] - -iam_users = { - user1 = { - groups = ["users", "admins"] - } -} - -manual_users = { - "user1@domain.email" = { - groups = ["users", "admins"] - } -} -``` - -### Assume role - -In order to gain the admin privileges necessary for infrastructure changes one needs to assume admin role. This will be -enabled only for user's defined in `input-params.tfvars`, only after logging into the AWS console for the first time as an -IAM user and enabling MFA. - -### Deploying remaining infra-blocks +### Deploying infra-blocks Once the state backend has been configured, provide/change the following inputs in `input-params.tfvars`. @@ -193,17 +159,12 @@ Required: - `state_bucket` - The name of the state bucket, as defined in `backend.hcl` - `data_bucket_name` - This will be the bucket where the data is going to be stored, it needs to be a unique name. - `log_bucket_name` - This is the bucket that will be used for logging, it needs to have a unique name. -- `iam_account_alias` - account alias required by AWS, it needs to be a unique name - `application_version` - service's docker image version - `ui_version` - The version of the static frontend site - `domain_name` - application hostname ([can be a domain or a subdomain](./domains_subdomains.md)) - `aws_account` - aws account id where the application will be hosted - `aws_region` - aws region where the application will be hosted -- `iam_users` - IAM users to be created automatically, with roles to be attached to them -- `manual_users` - IAM users that has been already created manually, with roles to be attached. (Can be left empty) -- `set_iam_user_groups` - User groups that need to be present on each user. (i.e. if the value is set to admin, then all - the users will require the admin role) - `support_emails_for_cloudwatch_alerts` - list of engineer emails that should receive alert notifications [more info](./alerting_monitoring.md) - `ip_whitelist` - ip range to add to application whitelist. The expected value is a list of strings. @@ -225,11 +186,9 @@ Then, run the following command on each block: Run the blocks in this order: -1. [iam-config](#iam-user-setup-optional) - > All the users' roles/policies will be handled here and will delete any previous config -2. vpc -3. s3 -4. auth -5. data-workflow -6. app-cluster -7. ui +1. vpc +2. s3 +3. auth +4. data-workflow +5. app-cluster +6. ui diff --git a/infrastructure/blocks/ecr/main.tf b/infrastructure/blocks/ecr/main.tf index 15ebdf81..00f963e5 100644 --- a/infrastructure/blocks/ecr/main.tf +++ b/infrastructure/blocks/ecr/main.tf @@ -4,6 +4,18 @@ terraform { } } +resource "aws_ecr_registry_scanning_configuration" "this" { + scan_type = "BASIC" + + rule { + scan_frequency = "SCAN_ON_PUSH" + repository_filter { + filter = "*" + filter_type = "WILDCARD" + } + } +} + resource "aws_ecr_repository" "private" { #checkov:skip=CKV_AWS_51:No need for immutable tags name = "data-f1-registry" @@ -55,58 +67,6 @@ resource "aws_ecr_lifecycle_policy" "image_expiry_policies" { EOF } - -resource "aws_ecr_repository" "private_ckan" { - #checkov:skip=CKV_AWS_51:No need for immutable tags - name = "rapid-ckan" - image_tag_mutability = "MUTABLE" - tags = var.tags - - image_scanning_configuration { - scan_on_push = true - } - - encryption_configuration { - encryption_type = "KMS" - } -} - -resource "aws_ecr_lifecycle_policy" "image_expiry_policies_ckan" { - repository = aws_ecr_repository.private_ckan.name - - policy = </dev/null || sudo apt install curl -y -curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg -sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg -echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null -sudo apt update -sudo apt install gh -y - -# Install V2 AWS CLI -curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" -unzip awscliv2.zip -sudo ./aws/install diff --git a/infrastructure/blocks/pipeline-ami/packer.tf b/infrastructure/blocks/pipeline-ami/packer.tf deleted file mode 100644 index 3b246320..00000000 --- a/infrastructure/blocks/pipeline-ami/packer.tf +++ /dev/null @@ -1,25 +0,0 @@ -resource "null_resource" "packer_build" { - triggers = { - sha256_ami_config = filesha256("${path.module}/template.json") - sha256_ami_install = filesha256("${path.module}/install.sh") - version = var.pipeline_ami_version - } - - provisioner "local-exec" { - command = < 0 ? 1 : 0 - name = "approved_amis_by_tag" - - source { - owner = "AWS" - source_identifier = "APPROVED_AMIS_BY_TAG" - } - - input_parameters = jsonencode({ - amisByTagKeyAndValue = var.amis_by_tag_key_and_value_list - }) - - depends_on = [aws_config_configuration_recorder.config] -} - -resource "aws_config_config_rule" "autoscaling_group_elb_healthcheck_required" { - name = "autoscaling_group_elb_healthcheck_required" - - source { - owner = "AWS" - source_identifier = "AUTOSCALING_GROUP_ELB_HEALTHCHECK_REQUIRED" - } - - depends_on = [aws_config_configuration_recorder.config] -} - -resource "aws_config_config_rule" "desired_instance_type" { - count = length(var.desired_instance_types) > 0 ? 1 : 0 - name = "desired_instance_type" - - source { - owner = "AWS" - source_identifier = "DESIRED_INSTANCE_TYPE" - } - - input_parameters = jsonencode({ - InstanceType = var.desired_instance_types - }) - - depends_on = [aws_config_configuration_recorder.config] -} - -resource "aws_config_config_rule" "rds_instance_public_access_check" { - name = "rds_instance_public_access_check" - - source { - owner = "AWS" - source_identifier = "RDS_INSTANCE_PUBLIC_ACCESS_CHECK" - } - - depends_on = [aws_config_configuration_recorder.config] -} - -resource "aws_config_config_rule" "rds_snapshots_public_prohibited" { - name = "rds_snapshots_public_prohibited" - - source { - owner = "AWS" - source_identifier = "RDS_SNAPSHOTS_PUBLIC_PROHIBITED" - } - - depends_on = [aws_config_configuration_recorder.config] -} - -resource "aws_config_config_rule" "iam_policy_no_statements_with_admin_access" { - name = "iam_policy_no_statements_with_admin_access" - - source { - owner = "AWS" - source_identifier = "IAM_POLICY_NO_STATEMENTS_WITH_ADMIN_ACCESS" - } - - depends_on = [aws_config_configuration_recorder.config] -} - -resource "aws_config_config_rule" "iam_root_access_key_check" { - name = "iam_root_access_key_check" - - source { - owner = "AWS" - source_identifier = "IAM_ROOT_ACCESS_KEY_CHECK" - } - - depends_on = [aws_config_configuration_recorder.config] -} diff --git a/infrastructure/modules/aws-core/config/outputs.tf b/infrastructure/modules/aws-core/config/outputs.tf deleted file mode 100644 index e0a6ea91..00000000 --- a/infrastructure/modules/aws-core/config/outputs.tf +++ /dev/null @@ -1,4 +0,0 @@ -output "config_s3_bucket_id" { - description = "The ID of the S3 bucket AWS Config writes its findings into" - value = var.enable_lifecycle_management_for_s3 ? aws_s3_bucket.config_with_lifecycle[0].id : aws_s3_bucket.config_without_lifecycle[0].id -} diff --git a/infrastructure/modules/aws-core/config/variables.tf b/infrastructure/modules/aws-core/config/variables.tf deleted file mode 100644 index 68229584..00000000 --- a/infrastructure/modules/aws-core/config/variables.tf +++ /dev/null @@ -1,99 +0,0 @@ -variable "bucket_prefix" { - type = string - description = "The prefix for the S3 bucket AWS Config Recorder writes to" - default = "aws-config" -} - -variable "bucket_key_prefix" { - type = string - description = "The prefix of the keys AWS Config writes to" - default = "aws_config" -} - -variable "config_recorder_name" { - type = string - description = "The name of the recorder for AWS Config" - default = "config" -} - -variable "config_delivery_channel_name" { - type = string - description = "The name of the delivery channel for AWS Config" - default = "config" -} - -variable "iam_role_name" { - type = string - description = "The name of the IAM role created for delegating permissions to AWS Config" - default = "config" -} - -variable "bucket_account_id" { - type = string - description = "The AWS account ID the S3 bucket lives in that AWS Config is writing its records to. Defaults to the ID of the current account" - default = "" -} - -variable "delivery_frequency" { - type = string - description = "The frequency at which AWS Config delivers its recorded findings to S3" - default = "Three_Hours" -} - -variable "password_policy" { - type = map(string) - description = "A map of values describing the password policy parameters AWS Config is looking for" - default = {} -} - -variable "iam_user_groups" { - type = map(string) - description = "A list of mandatory groups for IAM users" - default = {} -} - -variable "max_access_key_age" { - type = string - description = "The maximum amount of days an access key can live without being rotated" - default = "90" -} - -variable "enable_lifecycle_management_for_s3" { - type = bool - description = "Whether or not to enable lifecycle management for the S3 bucket AWS Config writes to" - default = true -} - -variable "amis_by_tag_key_and_value_list" { - type = list(string) - description = "Required AMI tags for EC2 instances" - default = [] -} - -variable "desired_instance_types" { - type = set(string) - description = "A string of comma-delimited instance types" - default = [] -} - -variable "s3_kms_sse_encryption_key_arn" { - type = string - description = "The ARN for the KMS key to use for S3 server-side bucket encryption" - default = "" # Use the default master key -} - -locals { - config_policy_arn = "arn:aws:iam::aws:policy/service-role/AWSConfigRole" - bucket_account_id = length(var.bucket_account_id) > 0 ? var.bucket_account_id : data.aws_caller_identity.current.account_id - password_policy = merge({ - require_uppercase_characters = true - require_lowercase_characters = true - require_symbols = true - require_numbers = true - minimum_password_length = 32 - password_reuse_prevention = 5 - max_password_age = 90 - }, var.password_policy) -} - -data "aws_caller_identity" "current" {} diff --git a/infrastructure/modules/aws-core/config/versions.tf b/infrastructure/modules/aws-core/config/versions.tf deleted file mode 100644 index c24371c1..00000000 --- a/infrastructure/modules/aws-core/config/versions.tf +++ /dev/null @@ -1,3 +0,0 @@ -terraform { - required_version = ">= 1.3.5" -} diff --git a/infrastructure/modules/aws-core/iam-resources/iam.tf b/infrastructure/modules/aws-core/iam-resources/iam.tf deleted file mode 100644 index 9e23136f..00000000 --- a/infrastructure/modules/aws-core/iam-resources/iam.tf +++ /dev/null @@ -1,204 +0,0 @@ -resource "aws_iam_account_alias" "iam_account_alias" { - count = var.set_iam_account_alias ? 1 : 0 - account_alias = var.iam_account_alias -} - -# Roles -data "aws_iam_policy_document" "admin_access_role_policy" { - statement { - effect = "Allow" - - actions = [ - "sts:AssumeRole", - ] - - condition { - test = "Bool" - variable = "aws:MultiFactorAuthPresent" - values = ["true"] - } - - condition { - test = "NumericLessThan" - variable = "aws:MultiFactorAuthAge" - values = [var.admin_multi_factor_auth_age] - } - - principals { - type = "AWS" - - identifiers = [ - "arn:aws:iam::${local.users_account_id}:root", - ] - } - } -} - -resource "aws_iam_role" "admin_access_role" { - name = var.admin_access_role_name - max_session_duration = var.admin_max_session_duration - assume_role_policy = data.aws_iam_policy_document.admin_access_role_policy.json -} - -data "aws_iam_policy_document" "user_access_role_policy" { - statement { - effect = "Allow" - - actions = [ - "sts:AssumeRole", - ] - - condition { - test = "Bool" - variable = "aws:MultiFactorAuthPresent" - values = ["true"] - } - - condition { - test = "NumericLessThan" - variable = "aws:MultiFactorAuthAge" - values = [var.user_multi_factor_auth_age] - } - - principals { - type = "AWS" - - identifiers = [ - "arn:aws:iam::${local.users_account_id}:root", - ] - } - } -} - -resource "aws_iam_role" "user_access_role" { - name = var.user_access_role_name - - assume_role_policy = data.aws_iam_policy_document.user_access_role_policy.json -} - -data "aws_iam_policy_document" "user_access_policy_document" { - statement { - effect = "Allow" - - actions = [ - "iam:PassRole", - ] - - not_resources = [ - aws_iam_role.user_access_role.arn, - aws_iam_role.admin_access_role.arn - ] - } -} - -resource "aws_iam_policy" "user_access_policy" { - name = "user_access_policy" - description = "User access for roles" - - policy = data.aws_iam_policy_document.user_access_policy_document.json -} - -data "aws_iam_policy_document" "no_vpc_access_policy_document" { - statement { - effect = "Deny" - - actions = [ - "ec2:AcceptVpcPeeringConnection", - "ec2:AssociateDhcpOptions", - "ec2:AssociateRouteTable", - "ec2:AttachClassicLinkVpc", - "ec2:AttachInternetGateway", - "ec2:AttachVpnGateway", - "ec2:CreateCustomerGateway", - "ec2:CreateDhcpOptions", - "ec2:CreateFlowLogs", - "ec2:CreateInternetGateway", - "ec2:CreateNatGateway", - "ec2:CreateNetworkAcl", - "ec2:CreateNetworkAclEntry", - "ec2:CreateRoute", - "ec2:CreateRouteTable", - "ec2:CreateSubnet", - "ec2:CreateVpc", - "ec2:CreateVpcPeeringConnection", - "ec2:CreateVpnConnection", - "ec2:CreateVpnConnectionRoute", - "ec2:CreateVpnGateway", - "ec2:DeleteCustomerGateway", - "ec2:DeleteDhcpOptions", - "ec2:DeleteInternetGateway", - "ec2:DeleteNatGateway", - "ec2:DeleteNetworkAcl", - "ec2:DeleteNetworkAclEntry", - "ec2:DeleteRoute", - "ec2:DeleteRouteTable", - "ec2:DeleteSubnet", - "ec2:DeleteVpc", - "ec2:DeleteVpnConnection", - "ec2:DeleteVpnConnectionRoute", - "ec2:DeleteVpnGateway", - "ec2:DetachClassicLinkVpc", - "ec2:DetachInternetGateway", - "ec2:DetachVpnGateway", - "ec2:DisableVgwRoutePropagation", - "ec2:DisableVpcClassicLink", - "ec2:DisableVpcClassicLinkDnsSupport", - "ec2:DisassociateRouteTable", - "ec2:EnableVgwRoutePropagation", - "ec2:EnableVpcClassicLink", - "ec2:EnableVpcClassicLinkDnsSupport", - "ec2:ModifySubnetAttribute", - "ec2:ModifyVpcAttribute", - "ec2:ModifyVpcEndpoint", - "ec2:ModifyVpcPeeringConnectionOptions", - "ec2:MoveAddressToVpc", - "ec2:RejectVpcPeeringConnection", - "ec2:ReplaceNetworkAclAssociation", - "ec2:ReplaceNetworkAclEntry", - "ec2:ReplaceRoute", - "ec2:ReplaceRouteTableAssociation", - "ec2:RestoreAddressToClassic", - ] - - resources = ["*"] - } -} - -resource "aws_iam_policy" "user_no_vpc_access_policy" { - name = "user_no_vpc_access_policy" - description = "deny user access to VPC related commands" - - policy = data.aws_iam_policy_document.no_vpc_access_policy_document.json -} - -# Policy attachments for roles -resource "aws_iam_policy_attachment" "admin_access_policy_attachment" { - # checkov:skip=CKV_AWS_274: Allow for AWS Administator Access - name = "admin_access_policy_attachment" - roles = [aws_iam_role.admin_access_role.name] - policy_arn = local.administrator_access_policy_arn -} - -resource "aws_iam_policy_attachment" "user_access_policy_attachment" { - name = "user_access_policy_attachment" - roles = [aws_iam_role.user_access_role.name] - policy_arn = aws_iam_policy.user_access_policy.arn -} - -resource "aws_iam_policy_attachment" "user_access_no_vpc_access_policy_attachment" { - name = "user_access_no_vpc_access_policy_attachment" - roles = [aws_iam_role.user_access_role.name] - policy_arn = aws_iam_policy.user_no_vpc_access_policy.arn -} - -resource "aws_iam_policy_attachment" "user_access_iam_read_only_policy_attachment" { - name = "user_access_iam_read_only_policy_attachment" - roles = [aws_iam_role.user_access_role.name] - policy_arn = local.iam_read_only_access_policy_arn -} - -resource "aws_iam_policy_attachment" "user_access_power_user_policy_attachment" { - name = "user_access_power_user_policy_attachment" - roles = [aws_iam_role.user_access_role.name] - policy_arn = local.power_user_access_policy_arn -} diff --git a/infrastructure/modules/aws-core/iam-resources/outputs.tf b/infrastructure/modules/aws-core/iam-resources/outputs.tf deleted file mode 100644 index 3da8433b..00000000 --- a/infrastructure/modules/aws-core/iam-resources/outputs.tf +++ /dev/null @@ -1,19 +0,0 @@ -output "resource_admin_role_name" { - value = aws_iam_role.admin_access_role.name - description = "The name of the role users are able to assume to attain admin privileges" -} - -output "resource_admin_role_arn" { - value = aws_iam_role.admin_access_role.arn - description = "The ARN of the role users are able to assume to attain admin privileges" -} - -output "resource_user_role_name" { - value = aws_iam_role.user_access_role.name - description = "The name of the role users are able to assume to attain user privileges" -} - -output "resource_user_role_arn" { - value = aws_iam_role.user_access_role.arn - description = "The ARN of the role users are able to assume to attain user privileges" -} diff --git a/infrastructure/modules/aws-core/iam-resources/variables.tf b/infrastructure/modules/aws-core/iam-resources/variables.tf deleted file mode 100644 index dc3f269c..00000000 --- a/infrastructure/modules/aws-core/iam-resources/variables.tf +++ /dev/null @@ -1,55 +0,0 @@ -variable "iam_account_alias" { - type = string - description = "A globally unique identifier, human-readable for your AWS account" -} - -variable "users_account_id" { - type = string - description = "The account ID of where the users are living in" - default = "" -} - -variable "set_iam_account_alias" { - type = bool - description = "Whether or not to set the account alias (useful to set to false when iam-users and iam-resources module are being deployed into the same account)" - default = true -} - -variable "admin_multi_factor_auth_age" { - type = string - description = "The amount of time (in seconds) for a admin session to be valid" - default = "3600" -} - -variable "user_multi_factor_auth_age" { - type = string - description = "The amount of time (in seconds) for a user session to be valid" - default = "14400" -} - -variable "admin_access_role_name" { - type = string - description = "Name of the admin role" - default = "resource-admin" -} - -variable "user_access_role_name" { - type = string - description = "Name of the user role" - default = "resource-user" -} - -variable "admin_max_session_duration" { - type = string - description = "The maximum session duration for the admin role" - default = "3600" -} - -data "aws_caller_identity" "current" {} - -locals { - administrator_access_policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess" - iam_read_only_access_policy_arn = "arn:aws:iam::aws:policy/IAMReadOnlyAccess" - power_user_access_policy_arn = "arn:aws:iam::aws:policy/PowerUserAccess" - users_account_id = length(var.users_account_id) > 0 ? var.users_account_id : data.aws_caller_identity.current.account_id -} diff --git a/infrastructure/modules/aws-core/iam-resources/versions.tf b/infrastructure/modules/aws-core/iam-resources/versions.tf deleted file mode 100644 index 1ee80683..00000000 --- a/infrastructure/modules/aws-core/iam-resources/versions.tf +++ /dev/null @@ -1,4 +0,0 @@ - -terraform { - required_version = ">= 1.3.5" -} diff --git a/infrastructure/modules/aws-core/iam-users/iam.tf b/infrastructure/modules/aws-core/iam-users/iam.tf deleted file mode 100644 index 68d19ae5..00000000 --- a/infrastructure/modules/aws-core/iam-users/iam.tf +++ /dev/null @@ -1,236 +0,0 @@ -# This is a policy which lets you self-service your own access keys. -# The only condition is that you have a MFA enabled session -data "aws_iam_policy_document" "aws_access_key_self_service_policy" { - statement { - actions = [ - "iam:CreateAccessKey", - "iam:DeleteAccessKey", - "iam:ListAccessKeys", - "iam:UpdateAccessKey", - ] - - effect = "Allow" - - condition { - test = "Bool" - variable = "aws:MultiFactorAuthPresent" - values = ["true"] - } - - condition { - test = "NumericLessThan" - variable = "aws:MultiFactorAuthAge" - values = [var.multi_factor_auth_age] - } - - resources = [ - "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/$${aws:username}", - ] - } -} - -resource "aws_iam_policy" "aws_access_key_self_service" { - name = "aws_access_key_self_service" - description = "Policy for access key self service" - - policy = data.aws_iam_policy_document.aws_access_key_self_service_policy.json -} - -resource "aws_iam_account_password_policy" "strict" { - # checkov:skip=CKV_AWS_9:Let users determine their own password policy - # checkov:skip=CKV_AWS_11:Let users determine their own password policy - # checkov:skip=CKV_AWS_12:Let users determine their own password policy - # checkov:skip=CKV_AWS_13:Let users determine their own password policy - # checkov:skip=CKV_AWS_14:Let users determine their own password policy - # checkov:skip=CKV_AWS_15:Let users determine their own password policy - minimum_password_length = local.password_policy["minimum_password_length"] - max_password_age = local.password_policy["max_password_age"] - password_reuse_prevention = local.password_policy["password_reuse_prevention"] - require_lowercase_characters = local.password_policy["require_lowercase_characters"] - require_numbers = local.password_policy["require_numbers"] - require_uppercase_characters = local.password_policy["require_uppercase_characters"] - require_symbols = local.password_policy["require_symbols"] - allow_users_to_change_password = local.password_policy["allow_users_to_change_password"] -} - -# This allows users without MFA to at least get a few details about their own account -data "aws_iam_policy_document" "aws_list_iam_users_policy" { - statement { - effect = "Allow" - - actions = [ - "iam:GetAccountSummary", - "iam:ListAccountAliases", - "iam:ListGroupsForUser", - "iam:ListUsers", - ] - - resources = [ - "arn:aws:iam::${data.aws_caller_identity.current.account_id}:*", - ] - } - - statement { - actions = ["iam:GetUser"] - - resources = [ - "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/$${aws:username}", - ] - - effect = "Allow" - } -} - -resource "aws_iam_policy" "aws_list_iam_users" { - name = "aws_list_iam_users" - description = "Let users see the list of users" - - policy = data.aws_iam_policy_document.aws_list_iam_users_policy.json -} - -data "aws_iam_policy_document" "aws_mfa_self_service_policy" { - statement { - effect = "Allow" - - resources = [ - "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/$${aws:username}", - ] - - actions = [ - "iam:DeactivateMFADevice", - "iam:EnableMFADevice", - "iam:ResyncMFADevice", - "iam:ListVirtualMFADevices", - "iam:ListMFADevices", - ] - } - - statement { - effect = "Allow" - - resources = [ - "arn:aws:iam::${data.aws_caller_identity.current.account_id}:mfa/$${aws:username}", - ] - - actions = [ - "iam:CreateVirtualMFADevice", - "iam:DeleteVirtualMFADevice", - ] - } - - statement { - effect = "Allow" - - resources = [ - "arn:aws:iam::${data.aws_caller_identity.current.account_id}:mfa/*", - ] - - actions = [ - "iam:ListVirtualMFADevices", - "iam:ListMFADevices", - ] - } -} - -resource "aws_iam_policy" "aws_mfa_self_service" { - name = "aws_mfa_self_service" - description = "Policy for MFA self service" - - policy = data.aws_iam_policy_document.aws_mfa_self_service_policy.json -} - -resource "aws_iam_account_alias" "iam_account_alias" { - count = var.set_iam_account_alias ? 1 : 0 - account_alias = var.iam_account_alias -} - -# Users -resource "aws_iam_user" "users" { - # checkov:skip=CKV_AWS_273: Allow for iam defined users - for_each = var.iam_users - name = each.key -} - -resource "aws_iam_user_group_membership" "users_group_memberships" { - depends_on = [ - aws_iam_user.users, - aws_iam_group.groups - ] - for_each = var.iam_users - user = each.key - - groups = each.value["groups"] -} - -# Groups -resource "aws_iam_group" "groups" { - for_each = toset(concat( - local.admin_groups, - local.user_groups - )) - name = each.key -} - -# Group policy assignments -resource "aws_iam_policy_attachment" "users_mfa_self_service" { - name = "users_mfa_self_service" - groups = values(aws_iam_group.groups)[*].name - policy_arn = aws_iam_policy.aws_mfa_self_service.arn -} - -resource "aws_iam_policy_attachment" "users_access_key_self_service" { - name = "users_access_key_self_service" - groups = values(aws_iam_group.groups)[*].name - policy_arn = aws_iam_policy.aws_access_key_self_service.arn -} - -resource "aws_iam_policy_attachment" "users_list_iam_users" { - name = "users_list_iam_users" - groups = values(aws_iam_group.groups)[*].name - policy_arn = aws_iam_policy.aws_list_iam_users.arn -} - -# Group policies -data "aws_iam_policy_document" "assume_role_admin_access_group_policy_document" { - statement { - actions = [ - "sts:AssumeRole", - ] - - effect = "Allow" - - resources = [ - "arn:aws:iam::${local.resources_account_id}:role/${var.resource_admin_role_name}", - ] - } -} - -resource "aws_iam_group_policy" "assume_role_admin_access_group_policy" { - for_each = toset(local.admin_groups) - name = "admin_access_group_policy" - group = aws_iam_group.groups[each.key].id - - policy = data.aws_iam_policy_document.assume_role_admin_access_group_policy_document.json -} - -data "aws_iam_policy_document" "assume_role_users_access_group_policy_document" { - statement { - effect = "Allow" - - actions = [ - "sts:AssumeRole", - ] - - resources = [ - "arn:aws:iam::${local.resources_account_id}:role/${var.resource_user_role_name}" - ] - } -} - -resource "aws_iam_group_policy" "assume_role_users_access_group_policy" { - for_each = toset(local.user_groups) - name = "users_access_group_policy" - group = aws_iam_group.groups[each.key].id - - policy = data.aws_iam_policy_document.assume_role_users_access_group_policy_document.json -} diff --git a/infrastructure/modules/aws-core/iam-users/outputs.tf b/infrastructure/modules/aws-core/iam-users/outputs.tf deleted file mode 100644 index ca4088a5..00000000 --- a/infrastructure/modules/aws-core/iam-users/outputs.tf +++ /dev/null @@ -1,9 +0,0 @@ -output "admin_group_names" { - value = values(aws_iam_group.groups)[*].name - description = "The names of the admin groups" -} - -output "user_group_names" { - value = values(aws_iam_group.groups)[*].name - description = "The name of the user groups" -} diff --git a/infrastructure/modules/aws-core/iam-users/variables.tf b/infrastructure/modules/aws-core/iam-users/variables.tf deleted file mode 100644 index d1771a56..00000000 --- a/infrastructure/modules/aws-core/iam-users/variables.tf +++ /dev/null @@ -1,88 +0,0 @@ -variable "iam_account_alias" { - type = string - description = "A globally unique identifier, human-readable for your AWS account" -} - -variable "set_iam_account_alias" { - type = bool - description = "Whether or not to set the account alias (useful to set to false when iam-users and iam-resources module are being deployed into the same account)" - default = true -} - -variable "multi_factor_auth_age" { - type = string - description = "The amount of time (in seconds) for a user session to be valid" - default = "14400" -} - -variable "password_policy" { - type = map(string) - description = "A map of password policy parameters you want to set differently from the defaults" - default = {} -} - -variable "resources_account_id" { - type = string - description = "The account ID of the AWS account you want to start resources in" - default = "" -} - -variable "resource_admin_role_name" { - type = string - description = "The name of the administrator role one is supposed to assume in the resource account" - default = "resource-admin" -} - -variable "resource_user_role_name" { - type = string - description = "The name of the user role one is supposed to assume in the resource account" - default = "resource-user" -} - -variable "admin_group_name" { - type = string - description = "The name of the initial group created for administrators" - default = "admins" -} - -variable "user_group_name" { - type = string - description = "The name of the initial group created for users" - default = "users" -} - -variable "additional_admin_groups" { - type = list(string) - description = "A list of additional groups to create associated with administrative privileges" - default = [] -} - -variable "additional_user_groups" { - type = list(string) - description = "A list of additional groups to create associated with regular users" - default = [] -} - -variable "iam_users" { - type = map(map(list(string))) - description = "A list of maps of users and their groups. Default is to create no users." - default = {} -} - -data "aws_caller_identity" "current" {} - -locals { - resources_account_id = length(var.resources_account_id) > 0 ? var.resources_account_id : data.aws_caller_identity.current.account_id - password_policy = merge({ - require_uppercase_characters = true - require_lowercase_characters = true - require_symbols = true - require_numbers = true - minimum_password_length = 32 - password_reuse_prevention = 5 - max_password_age = 90 - allow_users_to_change_password = true # pragma: allowlist secret - }, var.password_policy) - admin_groups = compact(concat([var.admin_group_name], var.additional_admin_groups)) - user_groups = compact(concat([var.user_group_name], var.additional_user_groups)) -} diff --git a/infrastructure/modules/aws-core/iam-users/versions.tf b/infrastructure/modules/aws-core/iam-users/versions.tf deleted file mode 100644 index f3355dbf..00000000 --- a/infrastructure/modules/aws-core/iam-users/versions.tf +++ /dev/null @@ -1,4 +0,0 @@ - -terraform { - required_version = ">= 1.5" -} diff --git a/infrastructure/modules/data-workflow/athena.tf b/infrastructure/modules/data-workflow/athena.tf index c8fd0bad..547f02a7 100644 --- a/infrastructure/modules/data-workflow/athena.tf +++ b/infrastructure/modules/data-workflow/athena.tf @@ -1,6 +1,5 @@ resource "aws_s3_bucket" "rapid_athena_query_results_bucket" { #checkov:skip=CKV_AWS_144:No need for cross region replication - #checkov:skip=CKV_AWS_145:No need for non default key #checkov:skip=CKV_AWS_21:No need to version query results #checkov:skip=CKV_AWS_18:No need to log query results #checkov:skip=CKV2_AWS_62:No need for event notifications @@ -15,7 +14,7 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "rapid_athena_quer rule { apply_server_side_encryption_by_default { - sse_algorithm = "AES256" + sse_algorithm = "aws:kms" } } } @@ -58,8 +57,7 @@ resource "aws_athena_workgroup" "rapid_athena_workgroup" { output_location = "s3://${aws_s3_bucket.rapid_athena_query_results_bucket.bucket}" encryption_configuration { - encryption_option = "SSE_S3" - # kms_key_arn = "" + encryption_option = "SSE_KMS" } } } diff --git a/infrastructure/modules/multihost-vpc/README.md b/infrastructure/modules/multihost-vpc/README.md deleted file mode 100644 index d0476738..00000000 Binary files a/infrastructure/modules/multihost-vpc/README.md and /dev/null differ diff --git a/infrastructure/modules/multihost-vpc/main.tf b/infrastructure/modules/multihost-vpc/main.tf deleted file mode 100644 index 3ac6a6ea..00000000 --- a/infrastructure/modules/multihost-vpc/main.tf +++ /dev/null @@ -1,112 +0,0 @@ -# Multihost VPC -resource "aws_vpc" "core" { - #checkov:skip=CKV2_AWS_11: No need for vpc flow logs - #checkov:skip=CKV2_AWS_12: Allow for non-restrictive vpc traffic - cidr_block = var.vpc_cidr_range - enable_dns_support = var.enable_dns_support - enable_dns_hostnames = var.enable_dns_hostnames - - tags = merge({ Name = var.vpc_name }, var.tags) -} - -# Public subnets -resource "aws_subnet" "public_subnet" { - # checkov:skip=CKV_AWS_130: Allow for public IP by default - count = length(data.aws_availability_zones.available.names) - vpc_id = aws_vpc.core.id - cidr_block = length(var.public_subnet_cidrs) > 0 ? var.public_subnet_cidrs[count.index] : cidrsubnet(var.vpc_cidr_range, var.public_subnet_size, count.index) - availability_zone = data.aws_availability_zones.available.names[count.index] - map_public_ip_on_launch = true - - tags = merge({ - Name = "${local.public_subnet_prefix}_${replace( - data.aws_availability_zones.available.names[count.index], - "-", - "_", - )}" - Scope = "public" }, - var.tags) -} - -# IGW - Internet Gateway -resource "aws_internet_gateway" "core_igw" { - vpc_id = aws_vpc.core.id - - tags = merge({ - Name = "${var.vpc_name}_igw" - }, var.tags) -} - -# EIPs for NAT Gateways -resource "aws_eip" "core_nat_gw_eip" { - count = length(data.aws_availability_zones.available.names) - domain = "vpc" - - depends_on = [aws_internet_gateway.core_igw] - - tags = merge({ - Name = "${var.vpc_name}_nat_gw_eip_${replace( - data.aws_availability_zones.available.names[count.index], - "-", - "_", - )}" - }, var.tags) -} - -# NAT Gateways for private subnets -resource "aws_nat_gateway" "core_nat_gw" { - count = length(data.aws_availability_zones.available.names) - subnet_id = aws_subnet.public_subnet[count.index].id - allocation_id = aws_eip.core_nat_gw_eip[count.index].id - - tags = merge({ - Name = "${var.vpc_name}_nat_gw_${replace( - data.aws_availability_zones.available.names[count.index], - "-", - "_", - )}" - }, var.tags) -} - -# Route tables for public/private subnets -resource "aws_route_table" "core_main_route_table" { - vpc_id = aws_vpc.core.id - - tags = merge({ - Name = "${var.vpc_name}_main_route_table_name" - }, var.tags) -} - -resource "aws_route_table" "core_private_route_table" { - count = length(data.aws_availability_zones.available.names) - vpc_id = aws_vpc.core.id - - tags = merge({ - Name = "${var.vpc_name}_private_route_table_${replace( - data.aws_availability_zones.available.names[count.index], - "-", - "_", - )}" - }, var.tags) -} - -# Default public route through the IGW -resource "aws_route" "core_main_route_table_public_default_route" { - route_table_id = aws_route_table.core_main_route_table.id - destination_cidr_block = "0.0.0.0/0" - gateway_id = aws_internet_gateway.core_igw.id -} - -# Default private route through the NAT gateways -resource "aws_route" "core_private_route_table_default_route" { - count = length(data.aws_availability_zones.available.names) - route_table_id = aws_route_table.core_private_route_table[count.index].id - destination_cidr_block = "0.0.0.0/0" - nat_gateway_id = aws_nat_gateway.core_nat_gw[count.index].id -} - -# Route table associations -resource "aws_main_route_table_association" "core_main_route_table_association" { - vpc_id = aws_vpc.core.id - route_table_id = aws_route_table.core_main_route_table.id -} diff --git a/infrastructure/modules/multihost-vpc/outputs.tf b/infrastructure/modules/multihost-vpc/outputs.tf deleted file mode 100644 index 9f395f63..00000000 --- a/infrastructure/modules/multihost-vpc/outputs.tf +++ /dev/null @@ -1,19 +0,0 @@ -output "vpc_id" { - value = aws_vpc.core.id - description = "The ID of the created VPC" -} - -output "vpc_cidr_range" { - value = aws_vpc.core.cidr_block - description = "The ID of the created VPC" -} - -output "private_route_table_ids" { - value = aws_route_table.core_private_route_table[*].id - description = "A list of private route table IDs" -} - -output "public_subnet_ids" { - value = aws_subnet.public_subnet[*].id - description = "A list of public subnet IDs" -} diff --git a/infrastructure/modules/multihost-vpc/variables.tf b/infrastructure/modules/multihost-vpc/variables.tf deleted file mode 100644 index 1d8335a3..00000000 --- a/infrastructure/modules/multihost-vpc/variables.tf +++ /dev/null @@ -1,54 +0,0 @@ -variable "tags" { - type = map(string) - description = "A map of tags to apply to all VPC resources" - default = {} -} - -variable "vpc_name" { - type = string - description = "The name of the VPC" - default = "core_vpc" -} - -variable "vpc_cidr_range" { - type = string - description = "The IP address space to use for the VPC" - default = "10.0.0.0/16" -} -# -variable "public_subnet_cidrs" { - type = list(string) - description = "A list of CIDRs for the public subnets. Needs to be the same amount as subnets in the Availability Zone you are deploying into (probably 3)" - default = [] -} - -variable "public_subnet_size" { - type = number - description = "The size of the public subnet (default: 1022 usable addresses)" - default = "6" -} - -variable "public_subnet_prefix" { - type = string - description = "The prefix to attach to the name of the public subnets" - default = "" -} - -variable "enable_dns_support" { - type = bool - description = "Whether or not to enable VPC DNS support" - default = true -} - -variable "enable_dns_hostnames" { - type = bool - description = "Whether or not to enable VPC DNS hostname support" - default = true -} -# -locals { - # We are using locals for this because there already is a fair amount of function calling inside the actual resources - public_subnet_prefix = length(var.public_subnet_prefix) > 0 ? var.public_subnet_prefix : "${var.vpc_name}_public_subnet" -} -# -data "aws_availability_zones" "available" {} diff --git a/infrastructure/modules/subnets/README.md b/infrastructure/modules/subnets/README.md deleted file mode 100644 index 0ef643c4..00000000 Binary files a/infrastructure/modules/subnets/README.md and /dev/null differ diff --git a/infrastructure/modules/subnets/main.tf b/infrastructure/modules/subnets/main.tf deleted file mode 100644 index e1231828..00000000 --- a/infrastructure/modules/subnets/main.tf +++ /dev/null @@ -1,23 +0,0 @@ -# Subnets -# Private subnets -resource "aws_subnet" "private_subnet" { - count = length(data.aws_availability_zones.available.names) - vpc_id = var.vpc_id - cidr_block = length(var.private_subnet_cidrs) > 0 ? var.private_subnet_cidrs[count.index] : cidrsubnet(var.vpc_cidr_range, var.private_subnet_size, var.private_subnet_offset + count.index) - availability_zone = data.aws_availability_zones.available.names[count.index] - - tags = merge({ - Name = "${local.private_subnet_prefix}_${replace( - data.aws_availability_zones.available.names[count.index], - "-", - "_", - )}" - Scope = "private" }, - var.tags) -} - -resource "aws_route_table_association" "core_private_route_table_association" { - count = length(data.aws_availability_zones.available.names) - subnet_id = aws_subnet.private_subnet[count.index].id - route_table_id = var.core_private_route_table_ids[count.index] -} diff --git a/infrastructure/modules/subnets/outputs.tf b/infrastructure/modules/subnets/outputs.tf deleted file mode 100644 index 24444878..00000000 --- a/infrastructure/modules/subnets/outputs.tf +++ /dev/null @@ -1,4 +0,0 @@ -output "private_subnet_ids" { - value = aws_subnet.private_subnet[*].id - description = "A list of private subnet IDs" -} diff --git a/infrastructure/modules/subnets/variables.tf b/infrastructure/modules/subnets/variables.tf deleted file mode 100644 index 4d23daa9..00000000 --- a/infrastructure/modules/subnets/variables.tf +++ /dev/null @@ -1,76 +0,0 @@ -variable "tags" { - type = map(string) - description = "A map of tags to apply to all VPC resources" - default = {} -} -# -variable "vpc_name" { - type = string - description = "The name of the VPC" - default = "core_vpc" -} -# -variable "vpc_cidr_range" { - type = string - description = "The IP address space to use for the VPC" -} - -variable "vpc_id" { - type = string - description = "The VPC ID of the core VPC" -} - -variable "core_private_route_table_ids" { - type = list(string) - description = "The private route table IDs of the core to route private subnets to the Nat gateways " -} - -variable "public_subnet_cidrs" { - type = list(string) - description = "A list of CIDRs for the public subnets. Needs to be the same amount as subnets in the Availability Zone you are deploying into (probably 3)" - default = [] -} - -variable "public_subnet_size" { - type = number - description = "The size of the public subnet (default: 1022 usable addresses)" - default = "6" -} - -variable "private_subnet_cidrs" { - type = list(string) - description = "A list of CIDRs for the private subnets. Needs to be the same amount as subnets in the Availability Zone you are deploying into (probably 3)" - default = [] -} - -variable "private_subnet_size" { - type = number - description = "The size of the private subnet (default: 1022 usable addresses)" - default = 6 -} - -variable "private_subnet_offset" { - type = number - description = "The amount of IP space between the public and the private subnet" - default = 32 -} - -variable "public_subnet_prefix" { - type = string - description = "The prefix to attach to the name of the public subnets" - default = "" -} - -variable "private_subnet_prefix" { - type = string - description = "The prefix to attach to the name of the private subnets" - default = "" -} - -locals { - # We are using locals for this because there already is a fair amount of function calling inside the actual resources - public_subnet_prefix = length(var.public_subnet_prefix) > 0 ? var.public_subnet_prefix : "${var.vpc_name}_public_subnet" - private_subnet_prefix = length(var.private_subnet_prefix) > 0 ? var.private_subnet_prefix : "${var.vpc_name}_private_subnet" -} - -data "aws_availability_zones" "available" {} diff --git a/infrastructure/modules/subnets/versions.tf b/infrastructure/modules/subnets/versions.tf deleted file mode 100644 index f3355dbf..00000000 --- a/infrastructure/modules/subnets/versions.tf +++ /dev/null @@ -1,4 +0,0 @@ - -terraform { - required_version = ">= 1.5" -} diff --git a/infrastructure/modules/ui/lambda.tf b/infrastructure/modules/ui/lambda.tf index 9dac1505..f3ed5ee0 100644 --- a/infrastructure/modules/ui/lambda.tf +++ b/infrastructure/modules/ui/lambda.tf @@ -29,10 +29,15 @@ data "aws_iam_policy_document" "this" { } } -resource "aws_iam_role_policy" "github_runner_lambda" { - name = "${var.resource-name-prefix}-cloudfront-router-policy" - role = aws_iam_role.this.name - policy = data.aws_iam_policy_document.this.json +resource "aws_iam_policy" "cloudfront_router_policy" { + name = "${var.resource-name-prefix}-cloudfront-router-policy" + description = "Managed policy for CloudFront Lambda@Edge router logging" + policy = data.aws_iam_policy_document.this.json +} + +resource "aws_iam_role_policy_attachment" "cloudfront_router_policy_attachment" { + role = aws_iam_role.this.name + policy_arn = aws_iam_policy.cloudfront_router_policy.arn } resource "aws_lambda_function" "this" {