浏览代码

Adds Portal to GC

Brad Poulton 4 年之前
父节点
当前提交
19b6f1bfc5

+ 1 - 0
base/customer_portal/amis.tf

@@ -0,0 +1 @@
+../amis.tf

+ 32 - 0
base/customer_portal/certificate.tf

@@ -0,0 +1,32 @@
+# waiting till ready to move over the DNS 
+#Certificate 
+# resource "aws_acm_certificate" "portal_cert" {
+#   domain_name       = "portal.${var.dns_info["public"]["zone"]}"
+#   validation_method = "DNS"
+
+#   tags = merge(var.standard_tags, var.tags)
+# }
+
+# resource "aws_acm_certificate_validation" "portal_cert" {
+#   certificate_arn         = aws_acm_certificate.portal_cert.arn
+#   validation_record_fqdns = [for record in aws_route53_record.portal_cert_validation: record.fqdn]
+# }
+
+# resource "aws_route53_record" "portal_cert_validation" {
+#   provider = aws.mdr-common-services-commercial
+
+#   for_each = {
+#     for dvo in aws_acm_certificate.portal_cert.domain_validation_options : dvo.domain_name => {
+#       name   = dvo.resource_record_name
+#       record = dvo.resource_record_value
+#       type   = dvo.resource_record_type
+#     }
+#   }
+
+#   allow_overwrite = true
+#   name            = each.value.name
+#   records         = [each.value.record]
+#   ttl             = 60
+#   type            = each.value.type
+#   zone_id         = var.dns_info["public"]["zone_id"]
+# }

+ 75 - 0
base/customer_portal/cloud-init/cloud-init.tpl

@@ -0,0 +1,75 @@
+#cloud-config
+preserve_hostname: false
+salt-master: ${salt_master}
+
+# Write files happens early
+write_files:
+- content: |
+    proxy=http://${proxy}:80
+  path: /etc/yum.conf
+  append: true
+- content: |
+    [global]
+    proxy=${proxy}
+  path: /etc/pip.conf
+- content: |
+    export HTTPS_PROXY=http://${proxy}:80
+    export HTTP_PROXY=http://${proxy}:80
+    export NO_PROXY=localhost,127.0.0.1,169.254.169.254,pvt.xdrtest.accenturefederalcyber.com,pvt.xdr.accenturefederalcyber.com,reposerver.msoc.defpoint.local,jenkins.msoc.defpoint.local,pod1search-splunk-sh.msoc.defpoint.local,s3.amazonaws.com,ssm.${ aws_region }.amazonaws.com,ec2messages.${ aws_region }.amazonaws.com,ec2.${ aws_region }.amazonaws.com,ssmmessages.${ aws_region }.amazonaws.com,iratemoses.mdr.defpoint.com,jira.mdr.defpoint.com,reposerver.pvt.xdr.accenturefederalcyber.com,jenkins.pvt.xdr.accenturefederalcyber.com,pod1search-splunk-sh.pvt.xdr.accenturefederalcyber.com,reposerver.pvt.xdrtest.accenturefederalcyber.com,jenkins.pvt.xdrtest.accenturefederalcyber.com,pod1search-splunk-sh.pvt.xdrtest.accenturefederalcyber.com,iratemoses.xdr.accenturefederalcyber.com,jira.xdr.accenturefederalcyber.com,iratemoses.xdrtest.accenturefederalcyber.com,jira.xdrtest.accenturefederalcyber.com
+    export https_proxy=$HTTPS_PROXY
+    export http_proxy=$HTTP_PROXY
+    export no_proxy=$NO_PROXY
+  path: /etc/profile.d/proxy.sh
+- content: |
+    master: ${salt_master}
+  path: /etc/salt/minion
+- content: |
+    grains:
+      environment: ${ environment }
+      aws_region: ${ aws_region }
+      aws_partition: ${ aws_partition }
+      aws_partition_alias: ${ aws_partition_alias }
+  path: /etc/salt/minion.d/cloud_init_grains.conf
+
+#yum_repos:
+#  epel-release:
+#    baseurl: http://download.fedoraproject.org/pub/epel/7/$basearch
+#    enabled: false
+#    failovermethod: priority
+#    gpgcheck: true
+#    gpgkey: http://download.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-7
+#    name: Extra Packages for Enterprise Linux 7 - Release
+
+packages:
+ - vim
+
+package_update: true # Always patch
+
+growpart:
+  mode: auto
+  devices: [ '/', '/var', '/var/log', '/var/log/audit', '/var/tmp', '/tmp', '/home' ]
+  ignore_growroot_disabled: false
+
+bootcmd:
+ - "INSTANCE_ID=`/usr/bin/curl -f --connect-timeout 1 --silent http://169.254.169.254/latest/meta-data/instance-id | tail -c 3`"
+ - "/bin/hostnamectl set-hostname customer-portal-$INSTANCE_ID'.${zone}'"
+ - "echo customer-portal-$INSTANCE_ID'.${zone}' > /etc/salt/minion_id"
+
+
+runcmd:
+ - /bin/systemctl restart salt-minion
+ - /bin/systemctl enable salt-minion
+ - /bin/systemctl start amazon-ssm-agent
+ - /bin/systemctl enable amazon-ssm-agent
+ - /usr/sbin/aide --update --verbose=0
+ - /bin/cp /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
+
+
+# Either final message or power state, but probably not both
+final_message: "The system is up after $UPTIME seconds"
+#power_state:
+#  delay: "+30"
+#  mode: reboot
+#  message: "System configured after $UPTIME seconds"
+#  timeout: 300
+#  condition: true

+ 124 - 0
base/customer_portal/ecr.tf

@@ -0,0 +1,124 @@
+locals {
+  registries = [
+    "portal_server",
+    "django_nginx", 
+  ]
+}
+
+data "aws_vpc_endpoint_service" "ecr_api_endpoint" {
+  service = "ecr.api"
+}
+
+data "aws_vpc_endpoint_service" "ecr_dkr_endpoint" {
+  service = "ecr.dkr"
+}
+
+# resource "aws_vpc_endpoint" "ecr_api_endpoint" {
+#   vpc_id            = var.vpc_id
+#   service_name      = data.aws_vpc_endpoint_service.ecr_api_endpoint.service_name
+#   vpc_endpoint_type = "Interface"
+
+#   subnet_ids = [ var.subnets ]
+#   security_group_ids = [ 
+#     "${aws_security_group.customer_portal_ecr.id}"
+#   ]
+#   private_dns_enabled = true 
+# }
+
+# resource "aws_vpc_endpoint" "ecr_dkr_endpoint" {
+#   vpc_id            = "${module.vpc.vpc_id}"
+#   service_name      = "${data.aws_vpc_endpoint_service.ecr_dkr_endpoint.service_name}"
+#   vpc_endpoint_type = "Interface"
+
+#   subnet_ids = [ "${module.vpc.public_subnets}" ]
+#   security_group_ids = [ 
+#     "${aws_security_group.customer_portal_ecr.id}"
+#   ]
+#   private_dns_enabled = true 
+# }
+
+resource "aws_iam_instance_profile" "portal_server_instance_profile" {
+  name     = "portal_server-instance-profile"
+  role     = aws_iam_role.portal_server.name
+}
+
+resource "aws_iam_role" "portal_server" {
+  name     = "portal-instance-role"
+
+  assume_role_policy = <<EOF
+{   
+    "Version": "2012-10-17",
+    "Statement": [
+      {   
+        "Sid": "", 
+        "Effect": "Allow",
+        "Principal": {
+          "Service": [
+            "ec2.amazonaws.com",
+            "ssm.amazonaws.com"
+            ]
+        },
+        "Action": "sts:AssumeRole"
+      }
+    ]
+  }
+EOF
+}
+
+data "aws_iam_policy_document" "portal_server_ecr_policy" {
+  statement {
+    actions = [
+      "ecr:GetAuthorizationToken",
+    ]
+
+    resources = ["*"]
+  }
+
+  statement {
+    sid    = "AllowCluCommunicationECR"
+    effect = "Allow"
+
+    actions = [
+			"ecr:BatchCheckLayerAvailability",
+			"ecr:GetDownloadUrlForLayer",
+			"ecr:GetRepositoryPolicy",
+			"ecr:DescribeRepositories",
+			"ecr:ListImages",
+			"ecr:DescribeImages",
+			"ecr:BatchGetImage",
+			"ecr:InitiateLayerUpload",
+			"ecr:UploadLayerPart",
+			"ecr:CompleteLayerUpload",
+			"ecr:PutImage"
+    ]
+
+    resources = [
+      "arn:aws-us-gov:ecr:us-gov-east-1:701290387780:repository/portal_server",
+      "arn:aws-us-gov:ecr:us-gov-east-1:701290387780:repository/django_nginx"
+    ]
+  }
+
+  statement {
+    sid    = "Tags"
+    effect = "Allow"
+
+    actions = [
+      "ec2:DescribeTags",
+      "ec2:DescribeInstances"
+    ]
+    resources = [
+      "*"
+    ]
+  }
+}
+
+resource "aws_iam_policy" "portal_server_ecr_policy" {
+  name     = "portal_server_ecr"
+  path     = "/"
+  policy   = data.aws_iam_policy_document.portal_server_ecr_policy.json
+}
+
+resource "aws_iam_role_policy_attachment" "portal_server_ecr" {
+  role       = aws_iam_role.portal_server.name
+  policy_arn = aws_iam_policy.portal_server_ecr_policy.arn
+}

+ 189 - 0
base/customer_portal/elb.tf

@@ -0,0 +1,189 @@
+locals {
+    #allow some users to view test portal
+    portal_test_whitelist = [
+    "12.245.107.250/32",   # DPS Office Legato
+    "12.204.167.162/32",   # DPS Office San Antonio
+    "54.86.98.62/32",      # DPS AWS User VPN
+    "75.138.227.80/32",    # Duane Waddle
+    "24.11.231.98/32",     # George Starcher
+    "99.151.37.185/32",    # Wesley Leonard
+    "70.106.200.157/32",   # John Reuther
+    "73.10.53.113/32",     # Rick Page Home
+    "50.21.207.50/32",     # Brad Poulton
+    "70.160.60.248/32",    # Brandon Naughton 
+    "99.56.213.129/32",    # Frederick Damstra
+  ]
+
+}
+
+# ---------------------------------------------------------------------------------------------------------------------
+# LOAD BALANCER FOR PORTAL
+# ---------------------------------------------------------------------------------------------------------------------
+data "aws_caller_identity" "current" {}
+
+resource "aws_alb" "portal" {
+  name            = "portal-alb-${var.environment}"
+  security_groups = [ aws_security_group.customer_portal_alb.id, ]
+  internal        = false 
+  subnets         = var.subnets
+
+  tags = merge( var.standard_tags, var.tags, { Name = "portal-alb-${var.environment}" })
+
+  access_logs {
+    bucket = "xdr-elb-${ var.environment }"
+    prefix = ""
+    enabled = true
+  }
+}
+
+# Create a new target group
+resource "aws_alb_target_group" "portal" {
+  name                 = "portal-alb-targets-${var.environment}"
+  port                 = 443 
+  protocol             = "HTTPS"
+  vpc_id               = var.vpc_id
+
+  health_check {
+    protocol = "HTTPS"
+    path    = "/"
+    matcher = "200-400"
+  }
+
+  stickiness {
+    type    = "lb_cookie"
+    enabled = false 
+  }
+
+  tags = merge( var.standard_tags, var.tags, )
+}
+
+resource "aws_autoscaling_attachment" "portal" {
+  autoscaling_group_name = module.customer_portal_asg.this_autoscaling_group_name
+  alb_target_group_arn   = aws_alb_target_group.portal.arn
+}
+
+# Create a new alb listener ( certificate_arn wait for DNS cut over )
+resource "aws_alb_listener" "portal_https" {
+  load_balancer_arn = aws_alb.portal.arn
+  port              = "443"
+  protocol          = "HTTPS"
+  ssl_policy        = "ELBSecurityPolicy-TLS-1-2-2017-01"
+  #certificate_arn   = aws_acm_certificate_validation.portal_cert.certificate_arn
+
+  default_action {
+    target_group_arn = aws_alb_target_group.portal.arn
+    type             = "forward"
+  }
+}
+
+# resource "aws_alb_listener_certificate" "portal_https_cert" {
+#   certificate_arn   = data.aws_acm_certificate.portal_cert_v2.arn
+#   listener_arn      = aws_alb_listener.portal_https.arn
+# }
+ 
+
+
+# HTTPs Redirect
+resource "aws_lb_listener" "portal_https_redirect" {
+  load_balancer_arn = aws_alb.portal.arn
+  port              = "80"
+  protocol          = "HTTP"
+
+  default_action {
+    type = "redirect"
+
+    redirect {
+      port        = "443"
+      protocol    = "HTTPS"
+      status_code = "HTTP_301"
+    }
+  }
+}
+
+#resource "aws_route53_record" "portal_cert_validation" {
+#  name    = "${aws_acm_certificate.portal_cert.domain_validation_options.0.resource_record_name}"
+#  type    = "${aws_acm_certificate.portal_cert.domain_validation_options.0.resource_record_type}"
+#  zone_id = "${data.terraform_remote_state.infra.public_zone}"
+#  records = ["${aws_acm_certificate.portal_cert.domain_validation_options.0.resource_record_value}"]
+#  ttl     = 60
+#}
+
+#resource "aws_acm_certificate" "portal_cert" {
+#  domain_name       = "portal.${data.terraform_remote_state.infra.private_zone2_name}"
+#  validation_method = "DNS"
+#
+#  tags = {
+#    Name = "portal-cert-${terraform.workspace}"
+#    Environment = "${terraform.workspace}"
+#  }
+#}
+
+#data "aws_acm_certificate" "portal_cert_v2" {
+#  domain = "portal.${terraform.workspace == "test" ? "xdrtest" : "xdr" }.accenturefederalcyber.com"
+#  most_recent = true
+#}
+
+#resource "aws_acm_certificate_validation" "portal_cert" {
+#  certificate_arn         = "${aws_acm_certificate.portal_cert.arn}"
+#  validation_record_fqdns = ["${aws_route53_record.portal_cert_validation.fqdn}"]
+#}
+
+#Wait to transfer the DNS until you are 100% ready!
+# DNS Alias for the LB
+# resource "aws_route53_record" "portal" {
+#   zone_id = var.dns_info["public"]["zone_id"]
+#   name    = "portal.${var.dns_info["public"]["zone_id"]}"
+#   records = [ aws_alb.portal.dns_name, ]
+#   type    = "CNAME"
+#   ttl     = 60
+# }
+
+# resource "aws_route53_record" "portal_private" {
+#   zone_id = var.dns_info["private"]["zone_id"]
+#   name    = "portal.${var.dns_info["private"]["zone_id"]}" 
+#   type    = "CNAME" 
+#   records = [ aws_alb.portal.dns_name, ]
+#   ttl     = 60
+# }
+
+#------------------------------------
+# Security Group
+#------------------------------------
+
+resource "aws_security_group" "customer_portal_alb" {
+  name        = "customer_portal_alb_inbound_sg"
+  description = "Allow Customer Portal ALB HTTP Traffic Inbound"
+  vpc_id      = var.vpc_id
+
+  ingress {
+    from_port   = 443
+    to_port     = 443
+    protocol    = "tcp"
+    cidr_blocks = [ var.environment == "test" ? "10.0.0.0/8" : "0.0.0.0/0",  ]
+  }
+
+  #Allow viewing of test portal from home. We don't want world to view test portal. 
+  ingress {
+    from_port   = 443
+    to_port     = 443
+    protocol    = "tcp"
+    cidr_blocks = flatten(local.portal_test_whitelist)
+  }
+
+  ## Needed for HTTPs redirect
+  ingress {
+    from_port   = 80
+    to_port     = 80
+    protocol    = "tcp"
+    cidr_blocks = [ var.environment == "test" ? "10.0.0.0/8" : "0.0.0.0/0", ]
+  }
+}
+
+resource "aws_security_group_rule" "customer_portal_alb" {
+  protocol                 = "tcp"
+  type                     = "egress"
+  from_port                = 443
+  to_port                  = 443
+  security_group_id        = aws_security_group.customer_portal_alb.id
+  source_security_group_id = aws_security_group.customer_portal.id
+}

+ 9 - 0
base/customer_portal/flowlogs.tf

@@ -0,0 +1,9 @@
+
+resource "aws_flow_log" "flowlogs" {
+  iam_role_arn    = "arn:${var.aws_partition}:iam::${var.aws_account_id}:role/aws_services/flowlogs"
+  log_destination = "arn:${var.aws_partition}:logs:${var.aws_region}:${var.aws_account_id}:log-group:vpc_flow_logs"
+
+  traffic_type    = "REJECT" # ALL is very noisy, and CIS only requires rejects.
+  vpc_id          = var.vpc_id
+  tags            = merge(var.standard_tags, var.tags)
+}

+ 334 - 22
base/customer_portal/main.tf

@@ -1,31 +1,343 @@
-#Certificate 
-resource "aws_acm_certificate" "portal_cert" {
-  domain_name       = "portal.${var.dns_info["public"]["zone"]}"
-  validation_method = "DNS"
+# Some instance variables
+locals {
+  ami_selection       = "minion" # master, minion, ...
+}
+
+# Rather than pass in the aws security group, we just look it up. This will
+# probably be useful other places, as well.
+data "aws_security_group" "typical-host" {
+  name   = "typical-host"
+  vpc_id = var.vpc_id
+}
+
+# Use the default EBS key
+data "aws_kms_key" "ebs-key" {
+  key_id = "alias/ebs_root_encrypt_decrypt"
+}
+
+#------------------------------------
+# EC2 ASG 
+#------------------------------------
+module "customer_portal_asg" {
+  source  = "terraform-aws-modules/autoscaling/aws"
+  version = "3.8.0"
+  name = "customer-portal"
+
+  lc_name = "customer-portal-lc"
 
-  tags = merge(var.standard_tags, var.tags)
+  iam_instance_profile = aws_iam_instance_profile.portal_server_instance_profile.name
+  image_id             = local.ami_map[local.ami_selection]
+  instance_type        = var.instance_type
+  security_groups      = [ data.aws_security_group.typical-host.id, aws_security_group.customer_portal.id ]
+  user_data            = data.template_cloudinit_config.cloud-init.rendered
+  key_name             = "msoc-build"
+  ebs_optimized        = true
+
+  root_block_device = [
+      {
+        volume_type = "gp2"
+        volume_size = "100"
+        delete_on_termination = true
+        encrypted = true
+        kms_key_id = data.aws_kms_key.ebs-key.arn
+      },
+  ]
+
+    ebs_block_device = [
+      {
+        # swap
+        device_name = "/dev/xvdm"
+        volume_size = 48
+        delete_on_termination = true
+        encrypted = true
+        kms_key_id = data.aws_kms_key.ebs-key.arn
+        # Snapshot IDs need to be grabbed from the ami, or it will replace every time. It's ugly.
+        # This may prompt replacement when the AMI is updated.
+        # See:
+        #   https://github.com/hashicorp/terraform/issues/19958
+        #   https://github.com/terraform-providers/terraform-provider-aws/issues/13118
+        snapshot_id = local.block_device_mappings[local.ami_selection]["/dev/xvdm"].ebs.snapshot_id
+      },
+      {
+        # /home
+        device_name = "/dev/xvdn"
+        # volume_size = xx
+        delete_on_termination = true
+        encrypted = true
+        kms_key_id = data.aws_kms_key.ebs-key.arn
+        snapshot_id = local.block_device_mappings[local.ami_selection]["/dev/xvdn"].ebs.snapshot_id
+      },
+      {
+        # /var
+        device_name = "/dev/xvdo"
+        # volume_size = xx
+        delete_on_termination = true
+        encrypted = true
+        kms_key_id = data.aws_kms_key.ebs-key.arn
+        snapshot_id = local.block_device_mappings[local.ami_selection]["/dev/xvdo"].ebs.snapshot_id
+      },
+      {
+        # /var/tmp
+        device_name = "/dev/xvdp"
+        # volume_size = xx
+        delete_on_termination = true
+        encrypted = true
+        kms_key_id = data.aws_kms_key.ebs-key.arn
+        snapshot_id = local.block_device_mappings[local.ami_selection]["/dev/xvdp"].ebs.snapshot_id
+      },
+      {
+        # /var/log
+        device_name = "/dev/xvdq"
+        # volume_size = xx
+        delete_on_termination = true
+        encrypted = true
+        kms_key_id = data.aws_kms_key.ebs-key.arn
+        snapshot_id = local.block_device_mappings[local.ami_selection]["/dev/xvdq"].ebs.snapshot_id
+      },
+      {
+        # /var/log/audit
+        device_name = "/dev/xvdr"
+        # volume_size = xx
+        delete_on_termination = true
+        encrypted = true
+        kms_key_id = data.aws_kms_key.ebs-key.arn
+        snapshot_id = local.block_device_mappings[local.ami_selection]["/dev/xvdr"].ebs.snapshot_id
+      },
+      {
+        # /tmp
+        device_name = "/dev/xvds"
+        # volume_size = xx
+        delete_on_termination = true
+        encrypted = true
+        kms_key_id = data.aws_kms_key.ebs-key.arn
+        snapshot_id = local.block_device_mappings[local.ami_selection]["/dev/xvds"].ebs.snapshot_id
+      },
+    ]
+
+
+  # Auto scaling group
+  asg_name                  = "customer-portal-asg"
+  vpc_zone_identifier       = [ element(var.subnets,0), element(var.subnets,1), element(var.subnets,2) ]
+  health_check_type         = "EC2"
+  min_size                  = 1
+  max_size                  = 2
+  desired_capacity          = 2
+  wait_for_capacity_timeout = 0
+  tags_as_map = merge(var.standard_tags, var.tags)
 }
 
-resource "aws_acm_certificate_validation" "portal_cert" {
-  certificate_arn         = aws_acm_certificate.portal_cert.arn
-  validation_record_fqdns = [for record in aws_route53_record.portal_cert_validation: record.fqdn]
+
+data "template_file" "cloud-init" {
+  # Should these be in a common directory? I suspect they'd be reusable
+  template = file("${path.module}/cloud-init/cloud-init.tpl")
+
+  vars = {
+    zone = var.dns_info["private"]["zone"]
+    environment = var.environment
+    salt_master  = var.salt_master
+    proxy = var.proxy
+    aws_partition = var.aws_partition
+    aws_partition_alias = var.aws_partition_alias
+    aws_region = var.aws_region
+  }
 }
 
-resource "aws_route53_record" "portal_cert_validation" {
-  provider = aws.mdr-common-services-commercial
+# Render a multi-part cloud-init config making use of the part
+# above, and other source files
+data "template_cloudinit_config" "cloud-init" {
+  gzip          = true
+  base64_encode = true
 
-  for_each = {
-    for dvo in aws_acm_certificate.portal_cert.domain_validation_options : dvo.domain_name => {
-      name   = dvo.resource_record_name
-      record = dvo.resource_record_value
-      type   = dvo.resource_record_type
-    }
+  # Main cloud-config configuration file.
+  part {
+    filename     = "init.cfg"
+    content_type = "text/cloud-config"
+    content      = data.template_file.cloud-init.rendered
   }
 
-  allow_overwrite = true
-  name            = each.value.name
-  records         = [each.value.record]
-  ttl             = 60
-  type            = each.value.type
-  zone_id         = var.dns_info["public"]["zone_id"]
+  # Additional parts as needed
+  #part {
+  #  content_type = "text/x-shellscript"
+  #  content      = "ffbaz"
+  #}
 }
+
+
+
+#------------------------------------
+# S3 Bucket  What is this used for?
+#------------------------------------
+resource "aws_s3_bucket" "customer-portal" {
+  bucket = "dps-customer-portal-${terraform.workspace}"
+  acl    = "private"
+
+  tags = merge(var.standard_tags, var.tags, )
+}
+
+#------------------------------------
+# Security Groups
+#------------------------------------
+
+resource "aws_security_group" "customer_portal" {
+  name        = "customer_portal_http_inbound_sg"
+  description = "Allow Customer Portal HTTP Inbound From ALB"
+  vpc_id      = var.vpc_id
+}
+
+resource "aws_security_group_rule" "customer_portal" {
+  protocol                 = "tcp"
+  type                     = "ingress"
+  from_port                = 443
+  to_port                  = 443
+  security_group_id        = aws_security_group.customer_portal.id
+  source_security_group_id = aws_security_group.customer_portal_alb.id
+}
+
+resource "aws_security_group" "customer_portal_ecr" {
+  name        = "ecr_customer_portal"
+  description = "Allow HTTPS outbound from portal to ECR"
+  vpc_id      = var.vpc_id
+}
+
+resource "aws_security_group_rule" "customer_portal_ecr_inbound" {
+  security_group_id = aws_security_group.customer_portal_ecr.id
+
+  type                     = "ingress"
+  from_port                = 443
+  to_port                  = 443
+  protocol                 = "tcp"
+  source_security_group_id = aws_security_group.customer_portal.id
+}
+
+resource "aws_security_group_rule" "customer_portal_postgres_outbound" {
+  security_group_id = aws_security_group.customer_portal.id
+
+  type                     = "egress"
+  from_port                = 5432
+  to_port                  = 5432
+  protocol                 = "tcp"
+  source_security_group_id = aws_security_group.postgres.id
+}
+
+#resource "aws_security_group_rule" "customer_portal_salt_outbound" {
+#  security_group_id = aws_security_group.customer_portal.id
+#
+#  type              = "egress"
+#  from_port         = 4505
+#  to_port           = 4506
+#  protocol          = "tcp"
+#  cidr_blocks              = var.salt_master_ip
+#}
+
+#resource "aws_security_group_rule" "customer_portal_gc_salt_outbound" {
+#  security_group_id = aws_security_group.customer_portal.id
+#
+#  type                     = "egress"
+#  from_port                = 4505
+#  to_port                  = 4506
+#  protocol                 = "tcp"
+#  cidr_blocks              = var.salt_master_ip
+#}
+
+#resource "aws_security_group_rule" "customer_portal_sensu_outbound" {
+#  security_group_id = aws_security_group.customer_portal.id
+#
+#  type                     = "egress"
+#  from_port                = 8081
+#  to_port                  = 8081
+#  protocol                 = "tcp"
+#  source_security_group_id = "${data.terraform_remote_state.infra.sensu_servers_sg}"
+#}
+
+resource "aws_security_group_rule" "customer_portal_http_outbound" {
+  security_group_id = aws_security_group.customer_portal.id
+
+  type        = "egress"
+  from_port   = 80
+  to_port     = 80
+  protocol    = "tcp"
+  cidr_blocks = ["0.0.0.0/0"]
+}
+
+resource "aws_security_group_rule" "customer_portal_https_outbound" {
+  security_group_id = aws_security_group.customer_portal.id
+
+  type        = "egress"
+  from_port   = 443
+  to_port     = 443
+  protocol    = "tcp"
+  cidr_blocks = ["0.0.0.0/0"]
+}
+
+# resource "aws_security_group_rule" "customer_portal_hec_outbound" {
+#   security_group_id = aws_security_group.customer_portal.id
+
+#   type        = "egress"
+#   from_port   = 8088
+#   to_port     = 8088
+#   protocol    = "tcp"
+#   cidr_blocks = ["${lookup(local.workspace-default-moose-idx-cidrs,terraform.workspace,"")}"]
+  
+#   description = "Outbound to Splunk Http Event Collector"
+# }
+
+# resource "aws_security_group_rule" "customer_portal_idxc_outbound" {
+#   security_group_id = aws_security_group.customer_portal.id
+
+#   type        = "egress"
+#   from_port   = 8089
+#   to_port     = 8089
+#   protocol    = "tcp"
+#   cidr_blocks = ["10.0.0.0/8"]
+#   description = "Outbound IDXC Discovery to MOOSE"
+# }
+
+#resource "aws_security_group_rule" "customer_portal_ssh_inbound" {
+#  security_group_id = "${aws_security_group.customer_portal.id}"
+#
+#  type                     = "ingress"
+#  from_port                = 22
+#  to_port                  = 22
+#  protocol                 = "tcp"
+#  cidr_blocks              = "${ local.access-server-cidrs[terraform.workspace] }"
+#}
+
+#resource "aws_security_group_rule" "customer_portal_ssh_inbound_openvpn" {
+#  security_group_id = "${aws_security_group.customer_portal.id}"
+#
+#  type                     = "ingress"
+#  from_port                = 22
+#  to_port                  = 22
+#  protocol                 = "tcp"
+#  source_security_group_id = "${data.terraform_remote_state.infra.openvpn_servers_sg}"
+#}
+
+#resource "aws_security_group_rule" "customer_portal_outbound_tcp_dns"
+#{
+#  type = "egress"
+#  from_port = 53
+#  to_port = 53
+#  protocol = "tcp"
+#  cidr_blocks = "${local.dns-server-cidrs[terraform.workspace]}"
+#  security_group_id = "${aws_security_group.customer_portal.id}"
+#  description = "Connect to unbound servers for dns"
+#}
+
+#resource "aws_security_group_rule" "customer_portal_outbound_udp_dns"
+#{
+#  type = "egress"
+#  from_port = 53
+#  to_port = 53
+#  protocol = "udp"
+#  cidr_blocks = "${local.dns-server-cidrs[terraform.workspace]}"
+#  security_group_id = "${aws_security_group.customer_portal.id}"
+#  description = "Connect to unbound servers for dns"
+#}
+
+
+
+### Output environment ID for purposes
+#output portal_env_id {
+#  value = "${aws_elastic_beanstalk_environment.mdr-customer-portal-env.id}"
+#}
+

+ 49 - 0
base/customer_portal/rds.tf

@@ -0,0 +1,49 @@
+#------------------------------------
+# RDS Cluster
+#------------------------------------
+resource "aws_kms_key" "customer_portal_kms" {
+  description = "RDS KMS Key"
+  enable_key_rotation = true
+}
+
+resource "aws_db_subnet_group" "customer_portal_rds_subnets" {
+  name        = "customer_portal_rds_subnets"
+  description = "Customer Portal RDS Private subnet"
+  subnet_ids  = [ element(var.subnets,0), element(var.subnets,1), element(var.subnets,2) ]
+}
+
+resource "aws_db_instance" "postgres" {
+  allocated_storage      = 20
+  storage_type           = "gp2"
+  engine                 = "postgres"
+  db_subnet_group_name   = aws_db_subnet_group.customer_portal_rds_subnets.name
+  vpc_security_group_ids = [ aws_security_group.postgres.id, ]
+  instance_class         = "db.t2.small"
+  identifier             = "customerportal"
+  name                   = "customerportal"
+  username               = "portal"
+  password               = "foobarbaz"
+  kms_key_id             = aws_kms_key.customer_portal_kms.arn
+  storage_encrypted      = "true"
+  ca_cert_identifier     = "rds-ca-2017"
+}
+
+#------------------------------------
+# Security Groups
+#------------------------------------
+
+resource "aws_security_group" "postgres" {
+  name        = "customer_portal_postgres_inbound_sg"
+  description = "Allow Customer Portal HTTP Traffic Inbound"
+  vpc_id      = var.vpc_id
+}
+
+resource "aws_security_group_rule" "customer_portal_postgres_inbound" {
+  security_group_id = aws_security_group.postgres.id
+
+  type        = "ingress"
+  from_port   = 5432
+  to_port     = 5432
+  protocol    = "tcp"
+  cidr_blocks = ["10.0.0.0/8"]
+}

+ 23 - 4
base/customer_portal/vars.tf

@@ -1,13 +1,32 @@
 variable "tags" { type = map }
-
 variable "dns_info" { type = map }
 variable "cidr_map" { type = map }
 variable "instance_termination_protection" { type = bool }
-variable "standard_tags" { type        = map }
-variable "environment" { type        = string }
+variable "standard_tags" { type = map }
+variable "environment" { type = string }
 variable "trusted_ips" { type = list }
 variable "aws_region" { type = string }
 variable "aws_partition" { type = string }
 variable "aws_partition_alias" { type = string }
 variable "aws_account_id" { type = string }
-variable "common_services_account" { type = string }
+variable "common_services_account" { type = string }
+variable "proxy" { type = string }
+variable "salt_master" { type = string }
+
+variable "instance_name" {
+  description = "Hostname, DNS entry, etc."
+  type = string
+}
+
+variable "instance_type" { 
+  type = string
+  default = "t3a.micro"
+}
+
+variable "vpc_id" {
+  type = string
+}
+
+variable "subnets" {
+  type = list(string)
+}

+ 71 - 0
base/customer_portal/waf.tf

@@ -0,0 +1,71 @@
+locals {
+  blacklisted_ips = [
+    {
+      "value" = "172.16.0.0/16"
+      type    = "IPV4"
+    },
+    {
+      "value" = "192.168.0.0/16"
+      type    = "IPV4"
+    },
+    {
+      "value" = "169.254.0.0/16"
+      type    = "IPV4"
+    },
+    {
+      "value" = "127.0.0.1/32"
+      type    = "IPV4"
+    },
+  ]
+
+# 73.10.53.113/32 Rick Page Home
+# 99.151.37.185/32 Wesley Leonard Home
+# 104.9.149.90/32 Greg Rivas Home
+# 100.4.76.3/32 Brandon Naughton Home
+# 170.248.173.247/32 AFS site
+# 170.248.173.245/32 AFS site
+# 70.120.41.230/32 Will Ledesma Home
+  admin_remote_ipset = [
+    {
+      "value" = "73.10.53.113/32"
+      type    = "IPV4"
+    },
+    {
+      "value" = "99.151.37.185/32"
+      type    = "IPV4"
+    },
+    {
+      "value" = "104.9.149.90/32"
+      type    = "IPV4"
+    },
+    {
+      "value" = "100.4.76.3/32"
+      type    = "IPV4"
+    },
+    {
+      "value" = "170.248.173.247/32"
+      type    = "IPV4"
+    },
+    {
+      "value" = "170.248.173.245/32"
+      type    = "IPV4"
+    },
+    {
+      "value" = "70.120.41.230/32"
+      type    = "IPV4"
+    },
+  ]
+  waf_prefix = "portal"
+}
+
+module "regional_waf" {
+  source             = "../../submodules/waf_owasp_top10"
+  waf_prefix         = local.waf_prefix
+  blacklisted_ips    = local.blacklisted_ips
+  admin_remote_ipset = local.admin_remote_ipset
+}
+
+resource "aws_wafregional_web_acl_association" "portal_alb_waf" {
+  resource_arn = aws_alb.portal.arn
+  web_acl_id   = module.regional_waf.web_acl_id
+}

+ 5 - 0
submodules/waf_owasp_top10/README.md

@@ -0,0 +1,5 @@
+This submodule was inspired by this module.
+https://github.com/binbashar/terraform-aws-waf-owasp
+
+https://registry.terraform.io/modules/binbashar/waf-owasp/aws/latest
+

+ 3 - 0
submodules/waf_owasp_top10/outputs.tf

@@ -0,0 +1,3 @@
+output "web_acl_id" {
+  value = aws_wafregional_web_acl.wafregional_acl.id
+}

+ 7 - 0
submodules/waf_owasp_top10/vars.tf

@@ -0,0 +1,7 @@
+variable "waf_prefix" {}
+variable "blacklisted_ips" {
+    type = list
+}
+variable "admin_remote_ipset" {
+    type = list
+}

+ 23 - 0
submodules/waf_owasp_top10/waf_condition_ip.tf

@@ -0,0 +1,23 @@
+resource "aws_wafregional_ipset" "admin_remote_ipset" {
+  name               = "${var.waf_prefix}-generic-match-admin-remote-ip"
+  dynamic ip_set_descriptor {
+    for_each = var.admin_remote_ipset
+
+    content {
+      type  = "IPV4"
+      value = ip_set_descriptor.value["value"]
+    }
+  }
+}
+
+resource "aws_wafregional_ipset" "blacklisted_ips" {
+  name               = "${var.waf_prefix}-generic-match-blacklisted-ips"
+  dynamic ip_set_descriptor {
+    for_each = var.blacklisted_ips
+
+    content {
+      type  = "IPV4"
+      value = ip_set_descriptor.value["value"]
+    }
+  }
+}

+ 59 - 0
submodules/waf_owasp_top10/waf_condition_size.tf

@@ -0,0 +1,59 @@
+resource "aws_wafregional_size_constraint_set" "size_restrictions" {
+  name = "${var.waf_prefix}-generic-size-restrictions"
+
+  size_constraints {
+    text_transformation = "NONE"
+    comparison_operator = "GT"
+    size                = "4096"
+
+    field_to_match {
+      type = "BODY"
+    }
+  }
+
+  size_constraints {
+    text_transformation = "NONE"
+    comparison_operator = "GT"
+    size                = "4093"
+
+    field_to_match {
+      type = "HEADER"
+      data = "cookie"
+    }
+  }
+
+  size_constraints {
+    text_transformation = "NONE"
+    comparison_operator = "GT"
+    size                = "1024"
+
+    field_to_match {
+      type = "QUERY_STRING"
+    }
+  }
+
+  size_constraints {
+    text_transformation = "NONE"
+    comparison_operator = "GT"
+    size                = "512"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+}
+
+resource "aws_wafregional_size_constraint_set" "csrf_token_set" {
+  name = "${var.waf_prefix}-generic-match-csrf-token"
+
+  size_constraints {
+    text_transformation = "NONE"
+    comparison_operator = "EQ"
+    size                = "118"
+
+    field_to_match {
+      type = "HEADER"
+      data = "cookie"
+    }
+  }
+}

+ 69 - 0
submodules/waf_owasp_top10/waf_condition_sql.tf

@@ -0,0 +1,69 @@
+resource "aws_wafregional_sql_injection_match_set" "sql_injection_match_set" {
+  name = "${var.waf_prefix}-generic-detect-sqli"
+
+  sql_injection_match_tuple {
+    text_transformation = "HTML_ENTITY_DECODE"
+
+    field_to_match {
+      type = "BODY"
+    }
+  }
+
+  sql_injection_match_tuple {
+    text_transformation = "URL_DECODE"
+
+    field_to_match {
+      type = "BODY"
+    }
+  }
+
+  sql_injection_match_tuple {
+    text_transformation = "HTML_ENTITY_DECODE"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+
+  sql_injection_match_tuple {
+    text_transformation = "URL_DECODE"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+
+  sql_injection_match_tuple {
+    text_transformation = "HTML_ENTITY_DECODE"
+
+    field_to_match {
+      type = "QUERY_STRING"
+    }
+  }
+
+  sql_injection_match_tuple {
+    text_transformation = "URL_DECODE"
+
+    field_to_match {
+      type = "QUERY_STRING"
+    }
+  }
+
+  sql_injection_match_tuple {
+    text_transformation = "HTML_ENTITY_DECODE"
+
+    field_to_match {
+      type = "HEADER"
+      data = "cookie"
+    }
+  }
+
+  sql_injection_match_tuple {
+    text_transformation = "URL_DECODE"
+
+    field_to_match {
+      type = "HEADER"
+      data = "cookie"
+    }
+  }
+}

+ 385 - 0
submodules/waf_owasp_top10/waf_condition_string_match.tf

@@ -0,0 +1,385 @@
+resource "aws_wafregional_byte_match_set" "match_admin_url" {
+  name = "${var.waf_prefix}-generic-match-admin-url"
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "/admin"
+    positional_constraint = "STARTS_WITH"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+}
+
+resource "aws_wafregional_byte_match_set" "match_saml_url" {
+  name = "${var.waf_prefix}-generic-match-saml-url"
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "/complete/saml"
+    positional_constraint = "STARTS_WITH"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+}
+
+resource "aws_wafregional_byte_match_set" "match_admin_company_url" {
+  name = "${var.waf_prefix}-generic-match-admin-company-url"
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "/admin/user_portal/company/"
+    positional_constraint = "STARTS_WITH"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+}
+
+resource "aws_wafregional_byte_match_set" "match_api_url" {
+  name = "${var.waf_prefix}-generic-match-api-url"
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "/api/"
+    positional_constraint = "STARTS_WITH"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+}
+
+resource "aws_wafregional_byte_match_set" "match_login_url" {
+  name = "${var.waf_prefix}-generic-match-login-url"
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "/login"
+    positional_constraint = "STARTS_WITH"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+}
+
+resource "aws_wafregional_byte_match_set" "match_auth_tokens" {
+  name = "${var.waf_prefix}-generic-match-auth-tokens"
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = ".TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"
+    positional_constraint = "ENDS_WITH"
+
+    field_to_match {
+      type = "HEADER"
+      data = "authorization"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "example-session-id"
+    positional_constraint = "CONTAINS"
+
+    field_to_match {
+      type = "HEADER"
+      data = "cookie"
+    }
+  }
+}
+
+resource "aws_wafregional_byte_match_set" "match_csrf_method" {
+  name = "${var.waf_prefix}-generic-match-csrf-method"
+
+  byte_match_tuples {
+    text_transformation   = "LOWERCASE"
+    target_string         = "post"
+    positional_constraint = "EXACTLY"
+
+    field_to_match {
+      type = "METHOD"
+    }
+  }
+}
+
+resource "aws_wafregional_byte_match_set" "match_php_insecure_uri" {
+  name = "${var.waf_prefix}-generic-match-php-insecure-uri"
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "php"
+    positional_constraint = "ENDS_WITH"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "/"
+    positional_constraint = "ENDS_WITH"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+}
+
+resource "aws_wafregional_byte_match_set" "match_php_insecure_var_refs" {
+  name = "${var.waf_prefix}-generic-match-php-insecure-var-refs"
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "_ENV["
+    positional_constraint = "CONTAINS"
+
+    field_to_match {
+      type = "QUERY_STRING"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "auto_append_file="
+    positional_constraint = "CONTAINS"
+
+    field_to_match {
+      type = "QUERY_STRING"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "disable_functions="
+    positional_constraint = "CONTAINS"
+
+    field_to_match {
+      type = "QUERY_STRING"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "auto_prepend_file="
+    positional_constraint = "CONTAINS"
+
+    field_to_match {
+      type = "QUERY_STRING"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "safe_mode="
+    positional_constraint = "CONTAINS"
+
+    field_to_match {
+      type = "QUERY_STRING"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "_SERVER["
+    positional_constraint = "CONTAINS"
+
+    field_to_match {
+      type = "QUERY_STRING"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "allow_url_include="
+    positional_constraint = "CONTAINS"
+
+    field_to_match {
+      type = "QUERY_STRING"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "open_basedir="
+    positional_constraint = "CONTAINS"
+
+    field_to_match {
+      type = "QUERY_STRING"
+    }
+  }
+}
+
+resource "aws_wafregional_byte_match_set" "match_rfi_lfi_traversal" {
+  name = "${var.waf_prefix}-generic-match-rfi-lfi-traversal"
+
+  byte_match_tuples {
+    text_transformation   = "HTML_ENTITY_DECODE"
+    target_string         = "://"
+    positional_constraint = "CONTAINS"
+
+    field_to_match {
+      type = "QUERY_STRING"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "HTML_ENTITY_DECODE"
+    target_string         = "../"
+    positional_constraint = "CONTAINS"
+
+    field_to_match {
+      type = "QUERY_STRING"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "://"
+    positional_constraint = "CONTAINS"
+
+    field_to_match {
+      type = "QUERY_STRING"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "../"
+    positional_constraint = "CONTAINS"
+
+    field_to_match {
+      type = "QUERY_STRING"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "HTML_ENTITY_DECODE"
+    target_string         = "://"
+    positional_constraint = "CONTAINS"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "HTML_ENTITY_DECODE"
+    target_string         = "../"
+    positional_constraint = "CONTAINS"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "://"
+    positional_constraint = "CONTAINS"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "../"
+    positional_constraint = "CONTAINS"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+}
+
+resource "aws_wafregional_byte_match_set" "match_ssi" {
+  name = "${var.waf_prefix}-generic-match-ssi"
+
+  byte_match_tuples {
+    text_transformation   = "LOWERCASE"
+    target_string         = ".cfg"
+    positional_constraint = "ENDS_WITH"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "LOWERCASE"
+    target_string         = ".backup"
+    positional_constraint = "ENDS_WITH"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "LOWERCASE"
+    target_string         = ".ini"
+    positional_constraint = "ENDS_WITH"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "LOWERCASE"
+    target_string         = ".conf"
+    positional_constraint = "ENDS_WITH"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "LOWERCASE"
+    target_string         = ".log"
+    positional_constraint = "ENDS_WITH"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "LOWERCASE"
+    target_string         = ".bak"
+    positional_constraint = "ENDS_WITH"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "LOWERCASE"
+    target_string         = ".config"
+    positional_constraint = "ENDS_WITH"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+
+  byte_match_tuples {
+    text_transformation   = "URL_DECODE"
+    target_string         = "/includes"
+    positional_constraint = "STARTS_WITH"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+}

+ 69 - 0
submodules/waf_owasp_top10/waf_condition_xss.tf

@@ -0,0 +1,69 @@
+resource "aws_wafregional_xss_match_set" "xss_match_set" {
+  name = "${var.waf_prefix}-generic-detect-xss"
+
+  xss_match_tuple {
+    text_transformation = "HTML_ENTITY_DECODE"
+
+    field_to_match {
+      type = "BODY"
+    }
+  }
+
+  xss_match_tuple {
+    text_transformation = "URL_DECODE"
+
+    field_to_match {
+      type = "BODY"
+    }
+  }
+
+  xss_match_tuple {
+    text_transformation = "HTML_ENTITY_DECODE"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+
+  xss_match_tuple {
+    text_transformation = "URL_DECODE"
+
+    field_to_match {
+      type = "URI"
+    }
+  }
+
+  xss_match_tuple {
+    text_transformation = "HTML_ENTITY_DECODE"
+
+    field_to_match {
+      type = "QUERY_STRING"
+    }
+  }
+
+  xss_match_tuple {
+    text_transformation = "URL_DECODE"
+
+    field_to_match {
+      type = "QUERY_STRING"
+    }
+  }
+
+  xss_match_tuple {
+    text_transformation = "HTML_ENTITY_DECODE"
+
+    field_to_match {
+      type = "HEADER"
+      data = "cookie"
+    }
+  }
+
+  xss_match_tuple {
+    text_transformation = "URL_DECODE"
+
+    field_to_match {
+      type = "HEADER"
+      data = "cookie"
+    }
+  }
+}

+ 163 - 0
submodules/waf_owasp_top10/waf_rule.tf

@@ -0,0 +1,163 @@
+resource "aws_wafregional_rule" "detect_admin_access" {
+  name        = "${var.waf_prefix}-generic-detect-admin-access"
+  metric_name = "${var.waf_prefix}genericdetectadminaccess"
+
+  predicate {
+    data_id = aws_wafregional_ipset.admin_remote_ipset.id
+    negated = true
+    type    = "IPMatch"
+  }
+
+  predicate {
+    data_id = aws_wafregional_byte_match_set.match_admin_url.id
+    negated = false
+    type    = "ByteMatch"
+  }
+}
+
+resource "aws_wafregional_rule" "detect_bad_auth_tokens" {
+  name        = "${var.waf_prefix}-generic-detect-bad-auth-tokens"
+  metric_name = "${var.waf_prefix}genericdetectbadauthtokens"
+
+  predicate {
+    data_id = aws_wafregional_byte_match_set.match_auth_tokens.id
+    negated = false
+    type    = "ByteMatch"
+  }
+}
+
+resource "aws_wafregional_rule" "detect_blacklisted_ips" {
+  name        = "${var.waf_prefix}-generic-detect-blacklisted-ips"
+  metric_name = "${var.waf_prefix}genericdetectblacklistedips"
+
+  predicate {
+    data_id = aws_wafregional_ipset.blacklisted_ips.id
+    negated = false
+    type    = "IPMatch"
+  }
+}
+
+resource "aws_wafregional_rule" "detect_php_insecure" {
+  name        = "${var.waf_prefix}-generic-detect-php-insecure"
+  metric_name = "${var.waf_prefix}genericdetectphpinsecure"
+
+  predicate {
+    data_id = aws_wafregional_byte_match_set.match_php_insecure_uri.id
+    negated = false
+    type    = "ByteMatch"
+  }
+
+  predicate {
+    data_id = aws_wafregional_byte_match_set.match_php_insecure_var_refs.id
+    negated = false
+    type    = "ByteMatch"
+  }
+}
+
+resource "aws_wafregional_rule" "detect_rfi_lfi_traversal" {
+  name        = "${var.waf_prefix}-generic-detect-rfi-lfi-traversal"
+  metric_name = "${var.waf_prefix}genericdetectrfilfitraversal"
+
+  predicate {
+    data_id = aws_wafregional_byte_match_set.match_rfi_lfi_traversal.id
+    negated = false
+    type    = "ByteMatch"
+  }
+}
+
+resource "aws_wafregional_rule" "detect_ssi" {
+  name        = "${var.waf_prefix}-generic-detect-ssi"
+  metric_name = "${var.waf_prefix}genericdetectssi"
+
+  predicate {
+    data_id = aws_wafregional_byte_match_set.match_ssi.id
+    negated = false
+    type    = "ByteMatch"
+  }
+}
+
+resource "aws_wafregional_rule" "enforce_csrf" {
+  name        = "${var.waf_prefix}-generic-enforce-csrf"
+  metric_name = "${var.waf_prefix}genericenforcecsrf"
+
+  predicate {
+    data_id = aws_wafregional_byte_match_set.match_csrf_method.id
+    negated = false
+    type    = "ByteMatch"
+  }
+
+  predicate {
+    data_id = aws_wafregional_size_constraint_set.csrf_token_set.id
+    negated = true
+    type    = "SizeConstraint"
+  }
+
+  predicate {
+    data_id = aws_wafregional_byte_match_set.match_saml_url.id
+    negated = true
+    type    = "ByteMatch"
+  }
+
+  predicate {
+    data_id = aws_wafregional_byte_match_set.match_api_url.id
+    negated = true
+    type    = "ByteMatch"
+  }
+
+  predicate {
+    data_id = aws_wafregional_byte_match_set.match_login_url.id
+    negated = true
+    type    = "ByteMatch"
+  }
+}
+
+resource "aws_wafregional_rule" "mitigate_sqli" {
+  name        = "${var.waf_prefix}-generic-mitigate-sqli"
+  metric_name = "${var.waf_prefix}genericmitigatesqli"
+
+  predicate {
+    data_id = aws_wafregional_sql_injection_match_set.sql_injection_match_set.id
+    negated = false
+    type    = "SqlInjectionMatch"
+  }
+}
+
+resource "aws_wafregional_rule" "mitigate_xss" {
+  name        = "${var.waf_prefix}-generic-mitigate-xss"
+  metric_name = "${var.waf_prefix}genericmitigatexss"
+
+  predicate {
+    data_id = aws_wafregional_xss_match_set.xss_match_set.id
+    negated = false
+    type    = "XssMatch"
+  }
+}
+
+resource "aws_wafregional_rule" "restrict_sizes" {
+  name        = "${var.waf_prefix}-generic-restrict-sizes"
+  metric_name = "${var.waf_prefix}genericrestrictsizes"
+
+  predicate {
+    data_id = aws_wafregional_size_constraint_set.size_restrictions.id
+    negated = false
+    type    = "SizeConstraint"
+  }
+
+  predicate {
+    data_id = aws_wafregional_byte_match_set.match_saml_url.id
+    negated = true
+    type    = "ByteMatch"
+  }
+
+  predicate {
+    data_id = aws_wafregional_byte_match_set.match_admin_company_url.id
+    negated = true
+    type    = "ByteMatch"
+  }
+
+  predicate {
+    data_id = aws_wafregional_byte_match_set.match_api_url.id
+    negated = true
+    type    = "ByteMatch"
+  }
+}

+ 114 - 0
submodules/waf_owasp_top10/waf_web_acl.tf

@@ -0,0 +1,114 @@
+data "aws_caller_identity" "current" {}
+
+resource "aws_wafregional_web_acl" "wafregional_acl" {
+  name        = "${var.waf_prefix}-generic-owasp-acl"
+  metric_name = "${var.waf_prefix}genericowaspacl"
+
+  logging_configuration {
+    log_destination = "arn:aws-us-gov:firehose:us-gov-east-1:${data.aws_caller_identity.current.account_id}:deliverystream/aws-waf-logs-splunk"
+  }
+
+  default_action {
+    type = "ALLOW"
+  }
+
+  rule {
+    action {
+      type = "BLOCK"
+    }
+
+    priority = 1
+    rule_id  = aws_wafregional_rule.restrict_sizes.id
+    type     = "REGULAR"
+  }
+
+  rule {
+    action {
+      type = "BLOCK"
+    }
+
+    priority = 2
+    rule_id  = aws_wafregional_rule.detect_blacklisted_ips.id
+    type     = "REGULAR"
+  }
+
+  rule {
+    action {
+      type = "BLOCK"
+    }
+
+    priority = 3
+    rule_id  = aws_wafregional_rule.detect_bad_auth_tokens.id
+    type     = "REGULAR"
+  }
+
+  rule {
+    action {
+      type = "BLOCK"
+    }
+
+    priority = 4
+    rule_id  = aws_wafregional_rule.mitigate_sqli.id
+    type     = "REGULAR"
+  }
+
+  rule {
+    action {
+      type = "BLOCK"
+    }
+
+    priority = 5
+    rule_id  = aws_wafregional_rule.mitigate_xss.id
+    type     = "REGULAR"
+  }
+
+  rule {
+    action {
+      type = "BLOCK"
+    }
+
+    priority = 6
+    rule_id  = aws_wafregional_rule.detect_rfi_lfi_traversal.id
+    type     = "REGULAR"
+  }
+
+  rule {
+    action {
+      type = "BLOCK"
+    }
+
+    priority = 7
+    rule_id  = aws_wafregional_rule.detect_php_insecure.id
+    type     = "REGULAR"
+  }
+
+  rule {
+    action {
+      type = "BLOCK"
+    }
+
+    priority = 8
+    rule_id  = aws_wafregional_rule.enforce_csrf.id
+    type     = "REGULAR"
+  }
+
+  rule {
+    action {
+      type = "BLOCK"
+    }
+
+    priority = 9
+    rule_id  = aws_wafregional_rule.detect_ssi.id
+    type     = "REGULAR"
+  }
+
+  rule {
+    action {
+      type = "BLOCK"
+    }
+
+    priority = 10
+    rule_id  = aws_wafregional_rule.detect_admin_access.id
+    type     = "REGULAR"
+  }
+}