wafv2 firewall rules to restrict domain names - amazon-web-services

I created WAFV2 firewall rules to restrict few IP address to hit API gateways. it is working as expected.
# ipsets
resource "aws_wafv2_ip_set" "example-ip-sets" {
name = "ipsetsexample"
description = "Example IP set"
scope = "REGIONAL"
ip_address_version = "IPV4"
addresses = ["137.137.43.154/32", "40.121.120.150/32"]
tags = {
Tag1 = "Value1"
Tag2 = "Value2"
}
}
resource "aws_wafv2_web_acl" "firewall" {
name = "firewall"
scope = "REGIONAL"
default_action {
block {}
}
rule {
name = "ip-blacklist"
priority = 1
action {
allow {}
}
statement {
ip_set_reference_statement {
arn = aws_wafv2_ip_set.example-ip-sets.arn
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "BlacklistedIP"
sampled_requests_enabled = true
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "Allowed"
sampled_requests_enabled = true
}
}
resource "aws_wafv2_web_acl_association" "this" {
resource_arn = aws_api_gateway_stage.api_stage.arn
web_acl_arn = aws_wafv2_web_acl.firewall.arn
}
but now new scenario came in to picture like, block some domain names instead of IP's (abcde.com, us1a.bcde.com) to invoke the API gateways. I have searched many terraform docs but didn't find any related.
is there any docs or source to restrict domain names to hit the API gateway ?

Related

Elastic Beanstalk setup with public ALB and EC2 on private subnet falling health check

I am trying to setup a sample Elastic beanstalk app with ALB being in public subnets(internet facing) and ec2 instances in private subnets in terraform. If I put ec2 instances in public subnets then the elastic beanstalk app get created successfully but in private subnets I get the following error.
The EC2 instances failed to communicate with AWS Elastic Beanstalk, either because of configuration problems with the VPC or a failed EC2 instance. Check your VPC configuration and try launching the environment again.
aws_elastic_beanstalk_environment
setting {
namespace = "aws:ec2:vpc"
name = "Subnets"
value = join(",", module.vpc.private_subnets)
}
setting {
namespace = "aws:ec2:vpc"
name = "DBSubnets"
value = join(",", module.vpc.private_subnets)
}
setting {
namespace = "aws:ec2:vpc"
name = "ELBSubnets"
value = join(",", module.vpc.public_subnets)
}
setting {
namespace = "aws:ec2:vpc"
name = "AssociatePublicIpAddress"
value = "false"
}
I have also setup vpc endpoints as describe in https://aws.amazon.com/premiumsupport/knowledge-center/elastic-beanstalk-instance-failure/
module "endpoints" {
source = "terraform-aws-modules/vpc/aws//modules/vpc-endpoints"
vpc_id = module.vpc.vpc_id
security_group_ids = [data.aws_security_group.default.id]
endpoints = {
dynamodb = {
service = "dynamodb",
service_type = "Gateway"
route_table_ids = module.vpc.private_route_table_ids
tags = { Name = "dynamodb-vpc-endpoint" }
},
s3 = {
service = "s3",
service_type = "Gateway"
route_table_ids = module.vpc.private_route_table_ids
tags = { Name = "s3-vpc-endpoint" }
},
elasticbeanstalk-app = {
# interface endpoint
service_name = aws_vpc_endpoint_service.elasticbeanstalk.service_name
subnet_ids = module.vpc.private_subnets
tags = { Name = "elasticbeanstalk-app-vpc-endpoint" }
},
elasticbeanstalk = {
# interface endpoint
service_name = "com.amazonaws.${var.aws_region}.elasticbeanstalk"
subnet_ids = module.vpc.private_subnets
private_dns_enabled = true
tags = { Name = "elasticbeanstalk-${var.aws_region}-elasticbeanstalk-vpc-endpoint" }
}
elasticbeanstalk-hc = {
# interface endpoint
service_name = "com.amazonaws.${var.aws_region}.elasticbeanstalk-health"
subnet_ids = module.vpc.private_subnets
private_dns_enabled = true
tags = { Name = "elasticbeanstalk-${var.aws_region}-elasticbeanstalk-health-vpc-endpoint" }
},
sqs = {
# interface endpoint
service_name = "com.amazonaws.${var.aws_region}.sqs"
subnet_ids = module.vpc.private_subnets
private_dns_enabled = true
tags = { Name = "elasticbeanstalk-${var.aws_region}-sqs-vpc-endpoint" }
},
cloudformation = {
# interface endpoint
service_name = "com.amazonaws.${var.aws_region}.cloudformation"
subnet_ids = module.vpc.private_subnets
private_dns_enabled = true
tags = { Name = "elasticbeanstalk-${var.aws_region}-cloudformation-vpc-endpoint" }
},
ec2 = {
# interface endpoint
service_name = "com.amazonaws.${var.aws_region}.ec2"
subnet_ids = module.vpc.private_subnets
private_dns_enabled = true
tags = { Name = "elasticbeanstalk-${var.aws_region}-ec2-vpc-endpoint" }
},
ec2messages = {
# interface endpoint
service_name = "com.amazonaws.${var.aws_region}.ec2messages"
subnet_ids = module.vpc.private_subnets
private_dns_enabled = true
tags = { Name = "elasticbeanstalk-${var.aws_region}-ec2messages-vpc-endpoint" }
},
}
}
I have a vpc endpoint even for the elasticbeanstalk-app .The setup based on AWS beanstalk PrivateLink not connecting .
Security group
data "aws_security_group" "default" {
name = "default"
vpc_id = module.vpc.vpc_id
}
data "aws_vpc_endpoint_service" "dynamodb" {
service = "dynamodb"
filter {
name = "service-type"
values = ["Gateway"]
}
}
data "aws_vpc_endpoint_service" "s3" {
service = "s3"
filter {
name = "service-type"
values = ["Gateway"]
}
}
In order to be able to connect to service endpoints such as com.amazonaws.[aws_region].elasticbeanstal or com.amazonaws.[aws_region].elasticbeanstalk-health you need to have a security group which allows HTTP/HTTPS inbound connection.
My assumption is that aws_security_group.default security group, which is referenced from a data block, is a default security group and it does not allow HTTP/HTTPS inbound connectivity.

Wafv2 with terraform. how to do I exclude rules?

I created a WAFV2 as a modules and this is part of my code
name = var.name
description = "WAFv2 ACL for ${var.name}"
scope = var.scope
default_action {
allow {}
}
visibility_config {
cloudwatch_metrics_enabled = true
sampled_requests_enabled = true
metric_name = var.name
}
dynamic "rule" {
for_each = var.managed_rules
content {
name = rule.value.name
priority = rule.value.priority
override_action {
dynamic "none" {
for_each = rule.value.override_action == "none" ? [1] : []
content {}
}
dynamic "count" {
for_each = rule.value.override_action == "count" ? [1] : []
content {}
}
}
But after setting up kinesis firehouse I noticed some requests are been blocked by WAFV2, can anyone help me to figure out how to exclude some of the AwsManagdRules been blocked? here are some examples of them.
"CrossSiteScripting_BODY", "GenericLFI_BODY"
but I tried something like this one below in the tableau server using the waf is this correct?
name = aws_wafv2_rule_group.aws-wafv2-tableau.name
arn = aws_wafv2_rule_group.aws-wafv2-tableau.arn
priority = 0
override_action = "allow"
excluded_rules = ["CrossSiteScripting_BODY","GenericLFI_BODY"]
}]```

When using Terraform with AWS, how can I set a rate limit on a specific URI path (or regex of a URI path) on an ALB

I am trying to rate limit requests to the forgot password change URL using WAFv2 rules attached to an ALB on Cloudfront.
What I think I need to do is..
Create two resources aws_wafv2_web_acl.afv2_rate_limit and another called aws_wafv2_regex_pattern_set.wafv2_password_url
Example of rate: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl
Example of regex: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_regex_pattern_set
Combine these into a rule group, call it aws_wafv2_rule_group.wafv2_rule_group_pw_rate_group
Example of group: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_rule
I've created the rate limit and the regex, but I am failing to create the rule group. I put this rule in to refer to the rate limit
rule {
name = "rate_limit"
priority = 1
action {
block {}
}
statement {
and_statement {
statement {
rule_group_reference_statement { # !!FIXME!! doesn't work
arn = aws_wafv2_web_acl.wafv2_rate_limit.arn
}
}
}
}
visibility_config {
cloudwatch_metrics_enabled = false
metric_name = "password_url"
sampled_requests_enabled = false
}
}
I get the error on the rule_group_reference_statement line:
Blocks of type "rule_group_reference_statement" are not expected here.
I can attach the rule group to the ALB.
of course, the first question is whether this is even the right way to go about it?!
thanks for any thoughts.
working!
resource "aws_wafv2_web_acl" "wafv2_alb_pw5pm_acl" {
name = "wafv2_alb_pw5pm-acl"
description = "prevent brute forcing password setting or changing"
scope = "REGIONAL" # if using this, no need to set provider
default_action {
allow {} # pass traffic until the rules trigger a block
}
rule {
name = "rate_limit_pw5pm"
priority = 1
action {
block {}
}
statement {
rate_based_statement {
#limit = 300 # 5 per sec = 300 per min
limit = 100 # smallest value for testing
aggregate_key_type = "IP"
scope_down_statement {
regex_pattern_set_reference_statement {
arn = aws_wafv2_regex_pattern_set.wafv2_password_uri.arn
text_transformation {
priority = 1
type = "NONE"
}
field_to_match {
uri_path {}
}
}
}
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "wafv2_alb_pw5pm_acl_rule_vis"
sampled_requests_enabled = false
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "wafv2_alb_pw5pm_acl_vis"
sampled_requests_enabled = false
}
tags = {
managedby = "terraform"
}
}
resource "aws_wafv2_web_acl_association" "web_acl_association_my_lb" {
resource_arn = aws_lb.xxxxxx.arn
web_acl_arn = aws_wafv2_web_acl.wafv2_alb_pw5pm_acl.arn
}
You can't nest a rule_group_reference_statement, for example for use inside a and_statement, not_statement or or_statement. It can only be referenced as a top-level statement within a rule.
yes you can. basically you need to declare an aws_wafv2_regex_pattern_set, in this example I use the URI "/api/*" but it can be a fixed one too.
resource "aws_wafv2_regex_pattern_set" "regex_pattern_api" {
name = "regex-path-api"
scope = "REGIONAL"
regular_expression {
regex_string = "/api/.+"
}
}
and the here is an example on how to use it on a waf declaration:
resource "aws_wafv2_web_acl" "waf" {
name = "waf"
scope = "REGIONAL"
default_action {
allow {}
}
rule {
name = "RateLimit"
priority = 1
action {
block {}
}
statement {
rate_based_statement {
aggregate_key_type = "IP"
limit = 100
scope_down_statement {
regex_pattern_set_reference_statement {
arn = aws_wafv2_regex_pattern_set.regex_pattern_api.arn
field_to_match {
uri_path {}
}
text_transformation {
priority = 1
type = "NONE"
}
}
}
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "RateLimit"
sampled_requests_enabled = true
}
}
visibility_config {
cloudwatch_metrics_enabled = false
metric_name = "waf"
sampled_requests_enabled = false
}
}
The cool part of this is that it is a rate limit that narrows the filter based on client IP using scope_down_statement

Google Cloud forwarding rule http -> https using terraform

I have setup forwarding rules, to map an URL onto my GCS Bucket using Terraform. Now, I am searching for a way to automatically forward all traffic from HTTP to HTTPS, so everybody reaching my page via HTTP automatically enters the secured page.
Any idea how I can do this using terraform? Below you can find all the code I used to set this up so far which is working perfectly fine. I just need this additional forwarding rule but don't know how to set this up. Any help would be highly appreciated.
locals {
static_bucket_name = "${var.environment}-${var.project_name}-static-pages"
domain_name = var.environment == "prd" ? "products.${project_name}.org" : "${var.environment}.products.${project_name}.org"
}
module "static-assets_cloud-storage-static-website" {
source = "gruntwork-io/static-assets/google//modules/cloud-storage-static-website"
version = "0.2.0"
website_domain_name = local.static_bucket_name
project = var.project_id
website_location = "EU"
force_destroy_access_logs_bucket = true
force_destroy_website = true
custom_labels = {
environment = var.environment
purpose = "static-site"
}
}
resource "google_compute_backend_bucket" "static_pages" {
name = local.static_bucket_name
description = "Contains static app assets"
bucket_name = module.static-assets_cloud-storage-static-website.website_bucket_name
enable_cdn = true
}
resource "google_compute_url_map" "static_pages" {
name = "${var.environment}-products"
default_service = google_compute_backend_bucket.static_pages.self_link
}
resource "google_compute_global_address" "static_pages" {
name = "${var.environment}-products-ip"
}
resource "google_compute_global_forwarding_rule" "http_to_static_pages" {
name = "${var.environment}-products-forward-rule"
target = google_compute_target_http_proxy.http_static_pages.self_link
ip_address = google_compute_global_address.static_pages.address
port_range = "80"
}
resource "google_compute_target_http_proxy" "http_static_pages" {
name = "${var.environment}-products-target-proxy"
url_map = google_compute_url_map.static_pages.self_link
}
resource "google_compute_target_https_proxy" "https_static_pages" {
project = var.project_id
name = "${var.environment}-products-target-proxy"
url_map = google_compute_url_map.static_pages.self_link
ssl_certificates = [google_compute_managed_ssl_certificate.static_pages.self_link]
}
resource "google_compute_global_forwarding_rule" "https_to_static_pages" {
name = "${var.environment}-products-https-forward-rule"
target = google_compute_target_https_proxy.https_static_pages.self_link
ip_address = google_compute_global_address.static_pages.address
port_range = "443"
}
resource "google_compute_managed_ssl_certificate" "static_pages" {
provider = google-beta
project = var.project_id
name = "${var.environment}-products-certificate"
managed {
domains = [local.domain_name]
}
}
```
Google supports this nicely with (only) three extra Terraform resources that create a second load balancer without backend but with a forwarding rule that just redirects to https.
The following is the (working) translation of their documentation:
resource "google_compute_url_map" "http-redirect" {
name = "http-redirect"
default_url_redirect {
redirect_response_code = "MOVED_PERMANENTLY_DEFAULT" // 301 redirect
strip_query = false
https_redirect = true // this is the magic
}
}
resource "google_compute_target_http_proxy" "http-redirect" {
name = "http-redirect"
url_map = google_compute_url_map.http-redirect.self_link
}
resource "google_compute_global_forwarding_rule" "http-redirect" {
name = "http-redirect"
target = google_compute_target_http_proxy.http-redirect.self_link
ip_address = google_compute_global_address.static_pages.address
port_range = "80"
}
I a little bit changed the Terraform code of Rutger de Knijf's answer above.
I removed "redirect_response_code" then the result is the same:
resource "google_compute_url_map" "http-redirect" {
name = "http-redirect"
default_url_redirect {
// "redirect_response_code" is removed
// redirect_response_code = "MOVED_PERMANENTLY_DEFAULT" // 301 redirect
strip_query = false
https_redirect = true // this is the magic
}
}
resource "google_compute_target_http_proxy" "http-redirect" {
name = "http-redirect"
url_map = google_compute_url_map.http-redirect.self_link
}
resource "google_compute_global_forwarding_rule" "http-redirect" {
name = "http-redirect"
target = google_compute_target_http_proxy.http-redirect.self_link
ip_address = google_compute_global_address.static_pages.address
port_range = "80"
}

is there any way I associate aws ELB/ALB with WAF ACL using terraform?

I created the following AWS WAF ACL and I want to associate it with my ALB using terraform. is there any way I can do it using terraform?
I want to block all requests except the ones that have secret key using amazon web service web application firewalls, aws waf. For that purpose, I created byte_set, aws rule and access control lists, ACL
resource "aws_alb" "app" {
............
}
#waf
resource "aws_waf_byte_match_set" "byte_set" {
name = "tf_waf_byte_match_set"
byte_match_tuples {
text_transformation = "NONE"
target_string = "${var.secret_key}"
positional_constraint = "EXACTLY"
field_to_match {
type = "HEADER"
data = "referer"
}
}
}
resource "aws_waf_rule" "wafrule" {
depends_on = ["aws_waf_byte_match_set.byte_set"]
name = "tfWAFRule"
metric_name = "tfWAFRule"
predicates {
data_id = "${aws_waf_byte_match_set.byte_set.id}"
negated = false
type = "ByteMatch"
}
}
resource "aws_waf_web_acl" "waf_acl" {
depends_on = ["aws_waf_byte_match_set.byte_set", "aws_waf_rule.wafrule"]
name = "tfWebACL"
metric_name = "tfWebACL"
default_action {
type = "BLOCK"
}
rules {
action {
type = "ALLOW"
}
priority = 1
rule_id = "${aws_waf_rule.wafrule.id}"
}
}
Sure, here is an example of the resource for the WAFv2 (I recommend to use this one) with a rate limit example rule and the association with an ALB:
########### This is the creation of an WAFv2 (Web ACL) and a example rate limit rule
resource "aws_wafv2_web_acl" "my_web_acl" {
name = "my-web-acl"
scope = "REGIONAL"
default_action {
allow {}
}
rule {
name = "RateLimit"
priority = 1
action {
block {}
}
statement {
rate_based_statement {
aggregate_key_type = "IP"
limit = 500
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "RateLimit"
sampled_requests_enabled = true
}
}
visibility_config {
cloudwatch_metrics_enabled = false
metric_name = "my-web-acl"
sampled_requests_enabled = false
}
}
########### This is the association code
resource "aws_wafv2_web_acl_association" "web_acl_association_my_lb" {
resource_arn = aws_lb.my_lb.arn
web_acl_arn = aws_wafv2_web_acl.my_web_acl.arn
}
You can associate a WAF with ALB (Application Load Balancer) and with CloudFront, you cannot associate with an ELB (Classic Elastic Load Balancer).
To associate with ALB this is the piece of code
resource "aws_wafregional_web_acl_association" "foo" {
resource_arn = "${aws_alb.foo.arn}"
web_acl_id = "${aws_wafregional_web_acl.foo.id}"
}
taken from the official documentation
This feature has been proposed but not merged yet.
https://github.com/hashicorp/terraform/issues/10713
https://github.com/hashicorp/terraform/pull/11263