Skip to content

Commit

Permalink
Block public Meadow access w/custom 403 response
Browse files Browse the repository at this point in the history
  • Loading branch information
mbklein committed Jul 1, 2024
1 parent 193813d commit 2df436e
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 63 deletions.
9 changes: 9 additions & 0 deletions firewall/ip_address_sets.tf
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
resource "aws_wafv2_ip_set" "nul_staff_ip_set" {
name = "nul-staff-ips"
description = "NU Library Staff IPv4 Addresses"
scope = "REGIONAL"
ip_address_version = "IPV4"
addresses = var.nul_staff_ips
tags = local.tags
}

resource "aws_wafv2_ip_set" "nul_ip_set" {
name = "nul-ips"
description = "NU Library IPv4 Addresses"
Expand Down
1 change: 1 addition & 0 deletions firewall/meadow_403.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>403 Forbidden</title><style>body{margin:0;color:#342f2e;background-color:#f5f5f5;font-weight:400;line-height:1.5;font-family:"Akkurat Pro Regular",-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif}header{background-color:#fff;border-bottom:1px solid #d8d6d6;min-height:4rem;display:flex;align-items:center}main{padding:3rem}footer{position:fixed;bottom:0;width:100%;display:flex;justify-content:center;background-color:#fff;padding:3rem}a{color:#4e2a84;text-decoration:underline;text-underline-offset:.1em;text-decoration-color:#e4e0ee}h1{margin:0 0 1rem}p{margin:0}.n{display:flex;padding:.5rem .75rem;fill:#401f68;height:1.75rem}.lockup{height:1.75rem;opacity:.5}</style></head><body><header><svg class="n" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 224.79 216.34"><title>Northwestern University Libraries</title><polygon points="142.76 1.62 142.76 16.87 168.36 23.31 173.41 29.64 173.41 151.91 45.73 .79 45.06 0 7.32 0 7.32 11.43 28.23 34.9 28.23 193.47 0 201.36 0 214.73 80.9 214.73 80.9 201.28 55.85 194.66 50.67 188.27 50.67 60.75 181.36 216.34 196.42 216.34 196.42 24.11 224.79 15.53 224.79 1.62 142.76 1.62"/></svg></header><main><h1>403 Forbidden</h1><p>Access to Meadow requires users to be on the campus network or logged into the <a href="https://services.northwestern.edu/TDClient/30/Portal/KB/ArticleDet?ID=1818">GlobalProtect VPN</a>.</p></main><footer><img class="lockup" alt="Northwestern University Libraries" src="https://iiif.dc.library.northwestern.edu/iiif/2/00000000-0000-0000-0000-000000000003/full/400,/0/default.webp"></footer></body></html>
188 changes: 125 additions & 63 deletions firewall/security_firewall.tf
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,124 @@ resource "aws_wafv2_web_acl" "security_firewall" {
content_type = "TEXT_PLAIN"
}

custom_response_body {
key = "meadow_access_denied"
content = file("${path.module}/meadow_403.html")
content_type = "TEXT_HTML"
}

rule {
name = "${local.namespace}-allow-honeybadger"
priority = 10

action {
allow {}
}

statement {
regex_match_statement {
regex_string = join("|", var.honeybadger_tokens)
field_to_match {
single_header {
name = "honeybadger-token"
}
}
text_transformation {
priority = 0
type = "NONE"
}
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${local.namespace}-allow-honeybadger"
sampled_requests_enabled = true
}
}

rule {
name = "${local.namespace}-allow-nul-staff-ips"
priority = 20

action {
allow {}
}

statement {
ip_set_reference_statement {
arn = aws_wafv2_ip_set.nul_staff_ip_set.arn
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${local.namespace}-allow-nul-staff-ips"
sampled_requests_enabled = true
}
}

rule {
name = "${local.namespace}-block-meadow-access"
priority = 30

action {
block {
custom_response {
custom_response_body_key = "meadow_access_denied"
response_code = 403
}
}
}

statement {
or_statement {
statement {
size_constraint_statement {
field_to_match {
single_header {
name = "host"
}
}

comparison_operator = "EQ"
size = 0

text_transformation {
priority = 0
type = "NONE"
}
}
}

statement {
byte_match_statement {
positional_constraint = "STARTS_WITH"
search_string = "meadow."
field_to_match {
single_header {
name = "host"
}
}
text_transformation {
priority = 0
type = "LOWERCASE"
}
}
}
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${local.namespace}-block-meadow-access"
sampled_requests_enabled = true
}
}

rule {
name = "${local.namespace}-allow-nul-ips"
priority = 0
priority = 40

rule_label {
name = "nul:internal-ip:v4"
Expand All @@ -56,7 +171,7 @@ resource "aws_wafv2_web_acl" "security_firewall" {

rule {
name = "${local.namespace}-${local.namespace}-allow-nul-ips-v6"
priority = 1
priority = 50

rule_label {
name = "nul:internal-ip:v6"
Expand All @@ -81,7 +196,7 @@ resource "aws_wafv2_web_acl" "security_firewall" {

rule {
name = "${local.namespace}-allowed-user-agents"
priority = 2
priority = 60

action {
allow {}
Expand Down Expand Up @@ -118,62 +233,9 @@ resource "aws_wafv2_web_acl" "security_firewall" {
}
}

# Reputation Lists
# Exempt the Meadow API from any rate limits defined later
rule {
name = "${local.namespace}-allow-meadow-api"
priority = 3

action {
allow {}
}

statement {
and_statement {
statement {
byte_match_statement {
positional_constraint = "CONTAINS"
search_string = "meadow"
field_to_match {
single_header {
name = "host"
}
}
text_transformation {
priority = 0
type = "LOWERCASE"
}
}
}

statement {
byte_match_statement {
positional_constraint = "STARTS_WITH"
search_string = "/api/"

field_to_match {
uri_path {}
}

text_transformation {
priority = 0
type = "NONE"
}
}
}
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${local.namespace}-allow-meadow-api"
sampled_requests_enabled = true
}
}

rule {
name = "${local.namespace}-aws-managed-ip-reputation-list"
priority = 4
priority = 80

override_action {
none {}
Expand All @@ -195,7 +257,7 @@ resource "aws_wafv2_web_acl" "security_firewall" {

rule {
name = "${local.namespace}-aws-managed-bot-control"
priority = 5
priority = 90

override_action {
none {}
Expand Down Expand Up @@ -229,7 +291,7 @@ resource "aws_wafv2_web_acl" "security_firewall" {

rule {
name = "${local.namespace}-high-traffic-ips"
priority = 6
priority = 100

action {
block {}
Expand All @@ -251,7 +313,7 @@ resource "aws_wafv2_web_acl" "security_firewall" {
# Challenge browsers that exceed the rate limit
rule {
name = "${local.namespace}-browser-rate-limiter"
priority = 7
priority = 110

action {
challenge {}
Expand Down Expand Up @@ -285,7 +347,7 @@ resource "aws_wafv2_web_acl" "security_firewall" {
# Rate limit (HTTP status 429) HTTP client libraries that exceed the rate limit
rule {
name = "${local.namespace}-http-client-rate-limiter"
priority = 8
priority = 120

action {
block {
Expand Down Expand Up @@ -319,7 +381,7 @@ resource "aws_wafv2_web_acl" "security_firewall" {

rule {
name = "${local.namespace}-aws-managed-common"
priority = 9
priority = 130

override_action {
none {}
Expand Down Expand Up @@ -353,7 +415,7 @@ resource "aws_wafv2_web_acl" "security_firewall" {

rule {
name = "${local.namespace}-aws-managed-known-bad-inputs"
priority = 10
priority = 140

override_action {
none {}
Expand Down
10 changes: 10 additions & 0 deletions firewall/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,21 @@ variable "global_rate_limit" {
default = 1000
}

variable "honeybadger_tokens" {
type = list(string)
default = []
}

variable "nul_ips" {
type = map(list(string))
default = {v4 = [], v6 = []}
}

variable "nul_staff_ips" {
type = list(string)
default = []
}

variable "resources" {
type = map
default = {}
Expand Down

0 comments on commit 2df436e

Please sign in to comment.