Refactor VPC firewall module for Terraform 1.3 (#949)

* module and module tests/examples

* align blueprints and fast

* fix null ranges

* make ports optional

* tfdoc

* make rules optional defaulting to all protocols

* review comments

* last round of comments

* invert precedence of template variables

* add option to disable all default rules

* add option to disable all default rules

* split egress/ingress

* tests

* fix tests
This commit is contained in:
Ludovico Magnocavallo 2022-11-04 13:56:07 +01:00 committed by GitHub
parent b166938435
commit fae5654e33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 935 additions and 933 deletions

View File

@ -67,17 +67,16 @@ module "firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.project.project_id project_id = module.project.project_id
network = module.vpc.name network = module.vpc.name
custom_rules = { ingress_rules = {
image-builder-ingress-builder-vm = { image-builder-ingress-builder-vm = {
description = "Allow image builder vm ingress traffic" description = "Allow image builder vm ingress traffic"
direction = "INGRESS" source_ranges = var.packer_source_cidrs
action = "allow"
sources = []
ranges = var.packer_source_cidrs
targets = [module.service-account-image-builder-vm.email] targets = [module.service-account-image-builder-vm.email]
use_service_accounts = true use_service_accounts = true
rules = [{ protocol = "tcp", ports = [22, 5985, 5986] }] rules = [{
extra_attributes = {} protocol = "tcp"
ports = [22, 5985, 5986]
}]
} }
} }
} }

View File

@ -66,24 +66,7 @@ module "landing-vpc" {
} }
module "landing-vpc-firewall" { module "landing-vpc-firewall" {
source = "../../../../modules/net-vpc-firewall" source = "../../../../modules/net-vpc-firewall"
project_id = module.landing-project.project_id project_id = module.landing-project.project_id
network = module.landing-vpc.name network = module.landing-vpc.name
admin_ranges = []
http_source_ranges = []
https_source_ranges = []
ssh_source_ranges = []
custom_rules = {
allow-ssh = {
description = "Allow SSH from IAP"
direction = "INGRESS"
action = "allow"
sources = []
ranges = ["35.235.240.0/20"]
targets = []
use_service_accounts = false
rules = [{ protocol = "tcp", ports = ["22"] }]
extra_attributes = {}
}
}
} }

View File

@ -128,11 +128,13 @@ module "vpc" {
} }
module "firewall" { module "firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
count = local.use_shared_vpc ? 0 : 1 count = local.use_shared_vpc ? 0 : 1
project_id = module.project.project_id project_id = module.project.project_id
network = module.vpc.0.name network = module.vpc.0.name
admin_ranges = ["10.0.0.0/20"] default_rules_config = {
admin_ranges = ["10.0.0.0/20"]
}
} }
module "nat" { module "nat" {

View File

@ -59,10 +59,12 @@ module "vpc" {
} }
module "vpc-firewall" { module "vpc-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.project-service.project_id project_id = module.project-service.project_id
network = module.vpc.name network = module.vpc.name
admin_ranges = [var.vpc_ip_cidr_range] default_rules_config = {
admin_ranges = [var.vpc_ip_cidr_range]
}
} }
############################################################################### ###############################################################################

View File

@ -118,11 +118,13 @@ module "load-vpc" {
} }
module "load-vpc-firewall" { module "load-vpc-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
count = local.use_shared_vpc ? 0 : 1 count = local.use_shared_vpc ? 0 : 1
project_id = module.load-project.project_id project_id = module.load-project.project_id
network = module.load-vpc.0.name network = module.load-vpc.0.name
admin_ranges = ["10.10.0.0/24"] default_rules_config = {
admin_ranges = ["10.10.0.0/24"]
}
} }
module "load-nat" { module "load-nat" {

View File

@ -133,11 +133,13 @@ module "orch-vpc" {
} }
module "orch-vpc-firewall" { module "orch-vpc-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
count = local.use_shared_vpc ? 0 : 1 count = local.use_shared_vpc ? 0 : 1
project_id = module.orch-project.project_id project_id = module.orch-project.project_id
network = module.orch-vpc.0.name network = module.orch-vpc.0.name
admin_ranges = ["10.10.0.0/24"] default_rules_config = {
admin_ranges = ["10.10.0.0/24"]
}
} }
module "orch-nat" { module "orch-nat" {

View File

@ -142,11 +142,13 @@ module "transf-vpc" {
} }
module "transf-vpc-firewall" { module "transf-vpc-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
count = local.use_shared_vpc ? 0 : 1 count = local.use_shared_vpc ? 0 : 1
project_id = module.transf-project.project_id project_id = module.transf-project.project_id
network = module.transf-vpc.0.name network = module.transf-vpc.0.name
admin_ranges = ["10.10.0.0/24"] default_rules_config = {
admin_ranges = ["10.10.0.0/24"]
}
} }
module "transf-nat" { module "transf-nat" {

View File

@ -72,22 +72,19 @@ module "vpc" {
} }
module "vpc-firewall" { module "vpc-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.project.project_id project_id = module.project.project_id
network = module.vpc.name network = module.vpc.name
admin_ranges = [var.vpc_config.ip_cidr_range] default_rules_config = {
custom_rules = { admin_ranges = [var.vpc_config.ip_cidr_range]
}
ingress_rules = {
#TODO Remove and rely on 'ssh' tag once terraform-provider-google/issues/9273 is fixed #TODO Remove and rely on 'ssh' tag once terraform-provider-google/issues/9273 is fixed
("${var.prefix}-iap") = { ("${var.prefix}-iap") = {
description = "Enable SSH from IAP on Notebooks." description = "Enable SSH from IAP on Notebooks."
direction = "INGRESS" source_ranges = ["35.235.240.0/20"]
action = "allow" targets = ["notebook-instance"]
sources = [] rules = [{ protocol = "tcp", ports = [22] }]
ranges = ["35.235.240.0/20"]
targets = ["notebook-instance"]
use_service_accounts = false
rules = [{ protocol = "tcp", ports = [22] }]
extra_attributes = {}
} }
} }
} }

View File

@ -27,11 +27,13 @@ module "vpc" {
} }
module "vpc-firewall" { module "vpc-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
count = local.use_shared_vpc ? 0 : 1 count = local.use_shared_vpc ? 0 : 1
project_id = module.project.project_id project_id = module.project.project_id
network = module.vpc[0].name network = module.vpc[0].name
admin_ranges = [var.vpc_subnet_range] default_rules_config = {
admin_ranges = [var.vpc_subnet_range]
}
} }
module "nat" { module "nat" {

View File

@ -76,69 +76,52 @@ module "vpc" {
} }
module "firewall" { module "firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = local.vpc_project project_id = local.vpc_project
network = local.network network = local.network
admin_ranges = [] default_rules_config = {
http_source_ranges = [] disabled = true
https_source_ranges = [] }
ssh_source_ranges = [] ingress_rules = {
custom_rules = {
"${local.prefix}allow-all-between-wsfc-nodes" = { "${local.prefix}allow-all-between-wsfc-nodes" = {
description = "Allow all between WSFC nodes" description = "Allow all between WSFC nodes"
direction = "INGRESS"
action = "allow"
sources = [module.compute-service-account.email] sources = [module.compute-service-account.email]
targets = [module.compute-service-account.email] targets = [module.compute-service-account.email]
ranges = []
use_service_accounts = true use_service_accounts = true
rules = [ rules = [
{ protocol = "tcp", ports = [] }, { protocol = "tcp" },
{ protocol = "udp", ports = [] }, { protocol = "udp" },
{ protocol = "icmp", ports = [] } { protocol = "icmp" }
] ]
extra_attributes = {}
} }
"${local.prefix}allow-all-between-wsfc-witness" = { "${local.prefix}allow-all-between-wsfc-witness" = {
description = "Allow all between WSFC witness nodes" description = "Allow all between WSFC witness nodes"
direction = "INGRESS"
action = "allow"
sources = [module.compute-service-account.email] sources = [module.compute-service-account.email]
targets = [module.witness-service-account.email] targets = [module.witness-service-account.email]
ranges = []
use_service_accounts = true use_service_accounts = true
rules = [ rules = [
{ protocol = "tcp", ports = [] }, { protocol = "tcp" },
{ protocol = "udp", ports = [] }, { protocol = "udp" },
{ protocol = "icmp", ports = [] } { protocol = "icmp" }
] ]
extra_attributes = {}
} }
"${local.prefix}allow-sql-to-wsfc-nodes" = { "${local.prefix}allow-sql-to-wsfc-nodes" = {
description = "Allow SQL connections to WSFC nodes" description = "Allow SQL connections to WSFC nodes"
direction = "INGRESS"
action = "allow"
sources = []
targets = [module.compute-service-account.email] targets = [module.compute-service-account.email]
ranges = var.sql_client_cidrs ranges = var.sql_client_cidrs
use_service_accounts = true use_service_accounts = true
rules = [ rules = [
{ protocol = "tcp", ports = [1433] }, { protocol = "tcp", ports = [1433] },
] ]
extra_attributes = {}
} }
"${local.prefix}allow-health-check-to-wsfc-nodes" = { "${local.prefix}allow-health-check-to-wsfc-nodes" = {
description = "Allow health checks to WSFC nodes" description = "Allow health checks to WSFC nodes"
direction = "INGRESS"
action = "allow"
sources = []
targets = [module.compute-service-account.email] targets = [module.compute-service-account.email]
ranges = var.health_check_ranges ranges = var.health_check_ranges
use_service_accounts = true use_service_accounts = true
rules = [ rules = [
{ protocol = "tcp", ports = [] }, { protocol = "tcp" }
] ]
extra_attributes = {}
} }
} }
} }

View File

@ -40,36 +40,36 @@ module "firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.host_project.project_id project_id = module.host_project.project_id
network = module.svpc.name network = module.svpc.name
custom_rules = merge({ allow-mesh = { ingress_rules = merge(
description = "Allow " {
direction = "INGRESS" allow-mesh = {
action = "allow" description = "Allow mesh."
sources = [] priority = 900
ranges = [for k, v in var.clusters_config : v.pods_cidr_block] source_ranges = [
targets = [for k, v in var.clusters_config : "${k}-node"] for k, v in var.clusters_config : v.pods_cidr_block
use_service_accounts = false ]
rules = [{ protocol = "tcp", ports = null }, targets = [
{ protocol = "udp", ports = null }, for k, v in var.clusters_config : "${k}-node"
{ protocol = "icmp", ports = null }, ]
{ protocol = "esp", ports = null }, rules = [
{ protocol = "ah", ports = null }, { protocol = "tcp" },
{ protocol = "sctp", ports = null }] { protocol = "udp" },
extra_attributes = { { protocol = "icmp" },
priority = 900 { protocol = "esp" },
} { protocol = "ah" },
} }, { protocol = "sctp" }
{ for k, v in var.clusters_config : "allow-${k}-istio" => { ]
description = "Allow "
direction = "INGRESS"
action = "allow"
sources = []
ranges = [v.master_cidr_block]
targets = ["${k}-node"]
use_service_accounts = false
rules = [{ protocol = "tcp", ports = [8080, 15014, 15017] }]
extra_attributes = {
priority = 1000
} }
},
{
for k, v in var.clusters_config : "allow-${k}-istio" => {
description = "Allow istio."
source_ranges = [v.master_cidr_block]
targets = ["${k}-node"]
rules = [{
protocol = "tcp"
ports = [8080, 15014, 15017]
}]
} }
} }
) )

View File

@ -74,17 +74,18 @@ module "firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.project-host.project_id project_id = module.project-host.project_id
network = module.vpc.name network = module.vpc.name
custom_rules = { ingress_rules = {
allow-ingress-squid = { allow-ingress-squid = {
description = "Allow squid ingress traffic" description = "Allow squid ingress traffic"
direction = "INGRESS" source_ranges = [
action = "allow" var.cidrs.apps, "35.191.0.0/16", "130.211.0.0/22"
sources = [] ]
ranges = [var.cidrs.apps, "35.191.0.0/16", "130.211.0.0/22"]
targets = [module.service-account-squid.email] targets = [module.service-account-squid.email]
use_service_accounts = true use_service_accounts = true
rules = [{ protocol = "tcp", ports = [3128] }] rules = [{
extra_attributes = {} protocol = "tcp"
ports = [3128]
}]
} }
} }
} }

View File

@ -69,10 +69,12 @@ module "nat-hub" {
} }
module "vpc-hub-firewall" { module "vpc-hub-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = var.project_id project_id = var.project_id
network = module.vpc-hub.name network = module.vpc-hub.name
admin_ranges = values(var.ip_ranges) default_rules_config = {
admin_ranges = values(var.ip_ranges)
}
} }
################################################################################ ################################################################################
@ -93,10 +95,12 @@ module "vpc-spoke-1" {
} }
module "vpc-spoke-1-firewall" { module "vpc-spoke-1-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.project.project_id project_id = module.project.project_id
network = module.vpc-spoke-1.name network = module.vpc-spoke-1.name
admin_ranges = values(var.ip_ranges) default_rules_config = {
admin_ranges = values(var.ip_ranges)
}
} }
module "nat-spoke-1" { module "nat-spoke-1" {
@ -138,10 +142,12 @@ module "vpc-spoke-2" {
} }
module "vpc-spoke-2-firewall" { module "vpc-spoke-2-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.project.project_id project_id = module.project.project_id
network = module.vpc-spoke-2.name network = module.vpc-spoke-2.name
admin_ranges = values(var.ip_ranges) default_rules_config = {
admin_ranges = values(var.ip_ranges)
}
} }
module "nat-spoke-2" { module "nat-spoke-2" {

View File

@ -39,10 +39,12 @@ module "dev-vpc" {
} }
module "dev-firewall" { module "dev-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = var.project_id project_id = var.project_id
network = module.dev-vpc.name network = module.dev-vpc.name
admin_ranges = values(var.ip_ranges) default_rules_config = {
admin_ranges = values(var.ip_ranges)
}
} }
module "dev-dns-peering" { module "dev-dns-peering" {

View File

@ -39,10 +39,12 @@ module "landing-vpc" {
} }
module "landing-firewall" { module "landing-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = var.project_id project_id = var.project_id
network = module.landing-vpc.name network = module.landing-vpc.name
admin_ranges = values(var.ip_ranges) default_rules_config = {
admin_ranges = values(var.ip_ranges)
}
} }
module "landing-dns-zone" { module "landing-dns-zone" {

View File

@ -39,10 +39,12 @@ module "prod-vpc" {
} }
module "prod-firewall" { module "prod-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = var.project_id project_id = var.project_id
network = module.prod-vpc.name network = module.prod-vpc.name
admin_ranges = values(var.ip_ranges) default_rules_config = {
admin_ranges = values(var.ip_ranges)
}
} }
module "prod-dns-peering" { module "prod-dns-peering" {

View File

@ -35,11 +35,13 @@ module "vpc-left" {
} }
module "firewall-left" { module "firewall-left" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.project.project_id project_id = module.project.project_id
network = module.vpc-left.name network = module.vpc-left.name
admin_ranges = values(var.ip_ranges) default_rules_config = {
ssh_source_ranges = ["35.235.240.0/20", "35.191.0.0/16", "130.211.0.0/22"] admin_ranges = values(var.ip_ranges)
ssh_ranges = ["35.235.240.0/20", "35.191.0.0/16", "130.211.0.0/22"]
}
} }
module "nat-left" { module "nat-left" {

View File

@ -46,11 +46,13 @@ module "vpc-right" {
} }
module "firewall-right" { module "firewall-right" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.project.project_id project_id = module.project.project_id
network = module.vpc-right.name network = module.vpc-right.name
admin_ranges = values(var.ip_ranges) default_rules_config = {
ssh_source_ranges = ["35.235.240.0/20", "35.191.0.0/16", "130.211.0.0/22"] admin_ranges = values(var.ip_ranges)
ssh_ranges = ["35.235.240.0/20", "35.191.0.0/16", "130.211.0.0/22"]
}
} }
module "nat-right" { module "nat-right" {

View File

@ -161,28 +161,22 @@ module "firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.project.project_id project_id = module.project.project_id
network = module.vpc.name network = module.vpc.name
custom_rules = { ingress_rules = {
format("%sallow-http-to-proxy-cluster", var.prefix) = { format("%sallow-http-to-proxy-cluster", var.prefix) = {
description = "Allow Nginx HTTP(S) ingress traffic" description = "Allow Nginx HTTP(S) ingress traffic"
direction = "INGRESS" source_ranges = [
action = "allow" var.cidrs[var.subnetwork], "35.191.0.0/16", "130.211.0.0/22"
sources = [] ]
ranges = [var.cidrs[var.subnetwork], "35.191.0.0/16", "130.211.0.0/22"]
targets = [module.service-account-proxy.email] targets = [module.service-account-proxy.email]
use_service_accounts = true use_service_accounts = true
rules = [{ protocol = "tcp", ports = [80, 443] }] rules = [{ protocol = "tcp", ports = [80, 443] }]
extra_attributes = {}
} }
format("%sallow-iap-ssh", var.prefix) = { format("%sallow-iap-ssh", var.prefix) = {
description = "Allow Nginx SSH traffic from IAP" description = "Allow Nginx SSH traffic from IAP"
direction = "INGRESS" source_ranges = ["35.235.240.0/20"]
action = "allow"
sources = []
ranges = ["35.235.240.0/20"]
targets = [module.service-account-proxy.email] targets = [module.service-account-proxy.email]
use_service_accounts = true use_service_accounts = true
rules = [{ protocol = "tcp", ports = [22] }] rules = [{ protocol = "tcp", ports = [22] }]
extra_attributes = {}
} }
} }
} }

View File

@ -69,11 +69,13 @@ module "vpc" {
} }
module "vpc-firewall" { module "vpc-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = var.project_id project_id = var.project_id
network = module.vpc.name network = module.vpc.name
admin_ranges = values(var.ip_ranges) default_rules_config = {
ssh_source_ranges = var.ssh_source_ranges admin_ranges = values(var.ip_ranges)
ssh_ranges = var.ssh_source_ranges
}
} }
module "vpn1" { module "vpn1" {

View File

@ -130,10 +130,12 @@ module "vpc-shared" {
} }
module "vpc-shared-firewall" { module "vpc-shared-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.project-host.project_id project_id = module.project-host.project_id
network = module.vpc-shared.name network = module.vpc-shared.name
admin_ranges = values(var.ip_ranges) default_rules_config = {
admin_ranges = values(var.ip_ranges)
}
} }
module "nat" { module "nat" {

View File

@ -12,18 +12,23 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
map(include('firewall_rule')) egress: map(include('firewall_rule'), required=False)
ingress: map(include('firewall_rule'), required=False)
--- ---
firewall_rule: firewall_rule:
description: str() deny: bool(required=False)
direction: enum("INGRESS", "EGRESS") description: str(required=False)
action: enum("allow", "deny") destination_ranges: list(str(), required=False)
sources: list(str()) disabled: bool(required=False)
ranges: list(str()) # enable_logging:
targets: list(str()) # include_metadata: bool(required=False)
use_service_accounts: bool() priority: int(required=False)
rules: list(include('rule')) source_ranges: list(str(), required=False)
sources: list(str(), required=False)
targets: list(str(), required=False)
use_service_accounts: bool(required=False)
rules: list(include('rule'), required=False)
--- ---
rule: rule:
protocol: enum("tcp", "udp", "all") protocol: str()
ports: list(num()) ports: list(num())

View File

@ -1,27 +1,21 @@
# skip boilerplate check # skip boilerplate check
ingress-allow-composer-nodes: ingress:
description: "Allow traffic to Composer nodes." ingress-allow-composer-nodes:
direction: INGRESS description: "Allow traffic to Composer nodes."
action: allow sources:
sources: - composer-worker
- composer-worker targets:
targets: - composer-worker
- composer-worker rules:
use_service_accounts: false - protocol: tcp
rules: ports: [80, 443, 3306, 3307]
- protocol: tcp ingress-allow-dataflow-load:
ports: [80, 443, 3306, 3307] description: "Allow traffic to Dataflow nodes."
sources:
ingress-allow-dataflow-load: - dataflow
description: "Allow traffic to Dataflow nodes." targets:
direction: INGRESS - dataflow
action: allow rules:
sources: - protocol: tcp
- dataflow ports: [12345, 12346]
targets:
- dataflow
use_service_accounts: false
rules:
- protocol: tcp
ports: [12345, 12346]

View File

@ -1,29 +1,19 @@
# skip boilerplate check # skip boilerplate check
allow-hc-nva-ssh-trusted: ingress:
description: "Allow traffic from Google healthchecks to NVA appliances" allow-hc-nva-ssh-trusted:
direction: INGRESS description: "Allow traffic from Google healthchecks to NVA appliances"
action: allow source_ranges:
sources: [] - healthchecks
ranges: rules:
- $healthchecks - protocol: tcp
targets: [] ports:
use_service_accounts: false - 22
rules: allow-onprem-probes-trusted-example:
- protocol: tcp description: "Allow traffic from onprem probes"
ports: source_ranges:
- 22 - onprem_probes
rules:
allow-onprem-probes-trusted-example: - protocol: tcp
description: "Allow traffic from onprem probes" ports:
direction: INGRESS - 12345
action: allow
sources: []
ranges:
- $onprem_probes
targets: []
use_service_accounts: false
rules:
- protocol: tcp
ports:
- 12345

View File

@ -1,15 +1,11 @@
# skip boilerplate check # skip boilerplate check
allow-hc-nva-ssh-untrusted: ingress:
description: "Allow traffic from Google healthchecks to NVA appliances" allow-hc-nva-ssh-untrusted:
direction: INGRESS description: "Allow traffic from Google healthchecks to NVA appliances"
action: allow source_ranges:
sources: [] - healthchecks
ranges: rules:
- $healthchecks - protocol: tcp
targets: [] ports:
use_service_accounts: false - 22
rules:
- protocol: tcp
ports:
- 22

View File

@ -57,15 +57,16 @@ module "landing-untrusted-vpc" {
} }
module "landing-untrusted-firewall" { module "landing-untrusted-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.landing-project.project_id project_id = module.landing-project.project_id
network = module.landing-untrusted-vpc.name network = module.landing-untrusted-vpc.name
admin_ranges = [] default_rules_config = {
http_source_ranges = [] disabled = true
https_source_ranges = [] }
ssh_source_ranges = [] factories_config = {
data_folder = "${var.data_dir}/firewall-rules/landing-untrusted" cidr_tpl_file = "${var.data_dir}/cidrs.yaml"
cidr_template_file = "${var.data_dir}/cidrs.yaml" rules_folder = "${var.data_dir}/firewall-rules/landing-untrusted"
}
} }
# NAT # NAT
@ -123,13 +124,14 @@ module "landing-trusted-vpc" {
} }
module "landing-trusted-firewall" { module "landing-trusted-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.landing-project.project_id project_id = module.landing-project.project_id
network = module.landing-trusted-vpc.name network = module.landing-trusted-vpc.name
admin_ranges = [] default_rules_config = {
http_source_ranges = [] disabled = true
https_source_ranges = [] }
ssh_source_ranges = [] factories_config = {
data_folder = "${var.data_dir}/firewall-rules/landing-trusted" cidr_tpl_file = "${var.data_dir}/cidrs.yaml"
cidr_template_file = "${var.data_dir}/cidrs.yaml" rules_folder = "${var.data_dir}/firewall-rules/landing-trusted"
}
} }

View File

@ -97,15 +97,16 @@ module "dev-spoke-vpc" {
} }
module "dev-spoke-firewall" { module "dev-spoke-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.dev-spoke-project.project_id project_id = module.dev-spoke-project.project_id
network = module.dev-spoke-vpc.name network = module.dev-spoke-vpc.name
admin_ranges = [] default_rules_config = {
http_source_ranges = [] disabled = true
https_source_ranges = [] }
ssh_source_ranges = [] factories_config = {
data_folder = "${var.data_dir}/firewall-rules/dev" cidr_tpl_file = "${var.data_dir}/cidrs.yaml"
cidr_template_file = "${var.data_dir}/cidrs.yaml" rules_folder = "${var.data_dir}/firewall-rules/dev"
}
} }
module "peering-dev" { module "peering-dev" {

View File

@ -97,15 +97,16 @@ module "prod-spoke-vpc" {
} }
module "prod-spoke-firewall" { module "prod-spoke-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.prod-spoke-project.project_id project_id = module.prod-spoke-project.project_id
network = module.prod-spoke-vpc.name network = module.prod-spoke-vpc.name
admin_ranges = [] default_rules_config = {
http_source_ranges = [] disabled = true
https_source_ranges = [] }
ssh_source_ranges = [] factories_config = {
data_folder = "${var.data_dir}/firewall-rules/prod" cidr_tpl_file = "${var.data_dir}/cidrs.yaml"
cidr_template_file = "${var.data_dir}/cidrs.yaml" rules_folder = "${var.data_dir}/firewall-rules/prod"
}
} }
module "peering-prod" { module "peering-prod" {

View File

@ -1,27 +1,21 @@
# skip boilerplate check # skip boilerplate check
ingress-allow-composer-nodes: ingress:
description: "Allow traffic to Composer nodes." ingress-allow-composer-nodes:
direction: INGRESS description: "Allow traffic to Composer nodes."
action: allow sources:
sources: - composer-worker
- composer-worker targets:
targets: - composer-worker
- composer-worker rules:
use_service_accounts: false - protocol: tcp
rules: ports: [80, 443, 3306, 3307]
- protocol: tcp ingress-allow-dataflow-load:
ports: [80, 443, 3306, 3307] description: "Allow traffic to Dataflow nodes."
sources:
ingress-allow-dataflow-load: - dataflow
description: "Allow traffic to Dataflow nodes." targets:
direction: INGRESS - dataflow
action: allow rules:
sources: - protocol: tcp
- dataflow ports: [12345, 12346]
targets:
- dataflow
use_service_accounts: false
rules:
- protocol: tcp
ports: [12345, 12346]

View File

@ -1,15 +1,11 @@
# skip boilerplate check # skip boilerplate check
allow-onprem-probes-example: ingress:
description: "Allow traffic from onprem probes" allow-onprem-probes-example:
direction: INGRESS description: "Allow traffic from onprem probes"
action: allow source_ranges:
sources: [] - onprem_probes
ranges: rules:
- $onprem_probes - protocol: tcp
targets: [] ports:
use_service_accounts: false - 12345
rules:
- protocol: tcp
ports:
- 12345

View File

@ -67,15 +67,16 @@ module "landing-vpc" {
} }
module "landing-firewall" { module "landing-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.landing-project.project_id project_id = module.landing-project.project_id
network = module.landing-vpc.name network = module.landing-vpc.name
admin_ranges = [] default_rules_config = {
http_source_ranges = [] disabled = true
https_source_ranges = [] }
ssh_source_ranges = [] factories_config = {
data_folder = "${var.data_dir}/firewall-rules/landing" cidr_tpl_file = "${var.data_dir}/cidrs.yaml"
cidr_template_file = "${var.data_dir}/cidrs.yaml" rules_folder = "${var.data_dir}/firewall-rules/landing"
}
} }
module "landing-nat-ew1" { module "landing-nat-ew1" {

View File

@ -67,15 +67,16 @@ module "dev-spoke-vpc" {
} }
module "dev-spoke-firewall" { module "dev-spoke-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.dev-spoke-project.project_id project_id = module.dev-spoke-project.project_id
network = module.dev-spoke-vpc.name network = module.dev-spoke-vpc.name
admin_ranges = [] default_rules_config = {
http_source_ranges = [] disabled = true
https_source_ranges = [] }
ssh_source_ranges = [] factories_config = {
data_folder = "${var.data_dir}/firewall-rules/dev" cidr_tpl_file = "${var.data_dir}/cidrs.yaml"
cidr_template_file = "${var.data_dir}/cidrs.yaml" rules_folder = "${var.data_dir}/firewall-rules/dev"
}
} }
module "dev-spoke-cloudnat" { module "dev-spoke-cloudnat" {

View File

@ -67,15 +67,16 @@ module "prod-spoke-vpc" {
} }
module "prod-spoke-firewall" { module "prod-spoke-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.prod-spoke-project.project_id project_id = module.prod-spoke-project.project_id
network = module.prod-spoke-vpc.name network = module.prod-spoke-vpc.name
admin_ranges = [] default_rules_config = {
http_source_ranges = [] disabled = true
https_source_ranges = [] }
ssh_source_ranges = [] factories_config = {
data_folder = "${var.data_dir}/firewall-rules/prod" cidr_tpl_file = "${var.data_dir}/cidrs.yaml"
cidr_template_file = "${var.data_dir}/cidrs.yaml" rules_folder = "${var.data_dir}/firewall-rules/prod"
}
} }
module "prod-spoke-cloudnat" { module "prod-spoke-cloudnat" {

View File

@ -1,27 +1,17 @@
# skip boilerplate check # skip boilerplate check
ingress-allow-composer-nodes: ingress:
description: "Allow traffic to Composer nodes." ingress-allow-composer-nodes:
direction: INGRESS description: "Allow traffic to Composer nodes."
action: allow targets:
sources: [] - composer-worker
ranges: ["0.0.0.0/0"] rules:
targets: - protocol: tcp
- composer-worker ports: [80, 443, 3306, 3307]
use_service_accounts: false ingress-allow-dataflow-load:
rules: description: "Allow traffic to Dataflow nodes."
- protocol: tcp targets:
ports: [80, 443, 3306, 3307] - dataflow
rules:
ingress-allow-dataflow-load: - protocol: tcp
description: "Allow traffic to Dataflow nodes." ports: [12345, 12346]
direction: INGRESS
action: allow
sources: []
ranges: ["0.0.0.0/0"]
targets:
- dataflow
use_service_accounts: false
rules:
- protocol: tcp
ports: [12345, 12346]

View File

@ -66,15 +66,16 @@ module "dev-spoke-vpc" {
} }
module "dev-spoke-firewall" { module "dev-spoke-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.dev-spoke-project.project_id project_id = module.dev-spoke-project.project_id
network = module.dev-spoke-vpc.name network = module.dev-spoke-vpc.name
admin_ranges = [] default_rules_config = {
http_source_ranges = [] disabled = true
https_source_ranges = [] }
ssh_source_ranges = [] factories_config = {
data_folder = "${var.data_dir}/firewall-rules/dev" cidr_tpl_file = "${var.data_dir}/cidrs.yaml"
cidr_template_file = "${var.data_dir}/cidrs.yaml" rules_folder = "${var.data_dir}/firewall-rules/dev"
}
} }
module "dev-spoke-cloudnat" { module "dev-spoke-cloudnat" {

View File

@ -66,15 +66,16 @@ module "prod-spoke-vpc" {
} }
module "prod-spoke-firewall" { module "prod-spoke-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.prod-spoke-project.project_id project_id = module.prod-spoke-project.project_id
network = module.prod-spoke-vpc.name network = module.prod-spoke-vpc.name
admin_ranges = [] default_rules_config = {
http_source_ranges = [] disabled = true
https_source_ranges = [] }
ssh_source_ranges = [] factories_config = {
data_folder = "${var.data_dir}/firewall-rules/prod" cidr_tpl_file = "${var.data_dir}/cidrs.yaml"
cidr_template_file = "${var.data_dir}/cidrs.yaml" rules_folder = "${var.data_dir}/firewall-rules/prod"
}
} }
module "prod-spoke-cloudnat" { module "prod-spoke-cloudnat" {

View File

@ -1,27 +1,21 @@
# skip boilerplate check # skip boilerplate check
ingress-allow-composer-nodes: ingress:
description: "Allow traffic to Composer nodes." ingress-allow-composer-nodes:
direction: INGRESS description: "Allow traffic to Composer nodes."
action: allow sources:
sources: - composer-worker
- composer-worker targets:
targets: - composer-worker
- composer-worker rules:
use_service_accounts: false - protocol: tcp
rules: ports: [80, 443, 3306, 3307]
- protocol: tcp ingress-allow-dataflow-load:
ports: [80, 443, 3306, 3307] description: "Allow traffic to Dataflow nodes."
sources:
ingress-allow-dataflow-load: - dataflow
description: "Allow traffic to Dataflow nodes." targets:
direction: INGRESS - dataflow
action: allow rules:
sources: - protocol: tcp
- dataflow ports: [12345, 12346]
targets:
- dataflow
use_service_accounts: false
rules:
- protocol: tcp
ports: [12345, 12346]

View File

@ -1,15 +1,11 @@
# skip boilerplate check # skip boilerplate check
allow-onprem-probes-example: ingress:
description: "Allow traffic from onprem probes" allow-onprem-probes-example:
direction: INGRESS description: "Allow traffic from onprem probes"
action: allow source_ranges:
sources: [] - onprem_probes
ranges: rules:
- $onprem_probes - protocol: tcp
targets: [] ports:
use_service_accounts: false - 12345
rules:
- protocol: tcp
ports:
- 12345

View File

@ -67,15 +67,16 @@ module "landing-vpc" {
} }
module "landing-firewall" { module "landing-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.landing-project.project_id project_id = module.landing-project.project_id
network = module.landing-vpc.name network = module.landing-vpc.name
admin_ranges = [] default_rules_config = {
http_source_ranges = [] disabled = true
https_source_ranges = [] }
ssh_source_ranges = [] factories_config = {
data_folder = "${var.data_dir}/firewall-rules/landing" cidr_tpl_file = "${var.data_dir}/cidrs.yaml"
cidr_template_file = "${var.data_dir}/cidrs.yaml" rules_folder = "${var.data_dir}/firewall-rules/landing"
}
} }
module "landing-nat-ew1" { module "landing-nat-ew1" {

View File

@ -67,15 +67,16 @@ module "dev-spoke-vpc" {
} }
module "dev-spoke-firewall" { module "dev-spoke-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.dev-spoke-project.project_id project_id = module.dev-spoke-project.project_id
network = module.dev-spoke-vpc.name network = module.dev-spoke-vpc.name
admin_ranges = [] default_rules_config = {
http_source_ranges = [] disabled = true
https_source_ranges = [] }
ssh_source_ranges = [] factories_config = {
data_folder = "${var.data_dir}/firewall-rules/dev" cidr_tpl_file = "${var.data_dir}/cidrs.yaml"
cidr_template_file = "${var.data_dir}/cidrs.yaml" rules_folder = "${var.data_dir}/firewall-rules/dev"
}
} }
module "dev-spoke-cloudnat" { module "dev-spoke-cloudnat" {

View File

@ -67,15 +67,16 @@ module "prod-spoke-vpc" {
} }
module "prod-spoke-firewall" { module "prod-spoke-firewall" {
source = "../../../modules/net-vpc-firewall" source = "../../../modules/net-vpc-firewall"
project_id = module.prod-spoke-project.project_id project_id = module.prod-spoke-project.project_id
network = module.prod-spoke-vpc.name network = module.prod-spoke-vpc.name
admin_ranges = [] default_rules_config = {
http_source_ranges = [] disabled = true
https_source_ranges = [] }
ssh_source_ranges = [] factories_config = {
data_folder = "${var.data_dir}/firewall-rules/prod" cidr_tpl_file = "${var.data_dir}/cidrs.yaml"
cidr_template_file = "${var.data_dir}/cidrs.yaml" rules_folder = "${var.data_dir}/firewall-rules/prod"
}
} }
module "prod-spoke-cloudnat" { module "prod-spoke-cloudnat" {

View File

@ -178,50 +178,28 @@ module "firewall" {
source = "./fabric/modules/net-vpc-firewall" source = "./fabric/modules/net-vpc-firewall"
project_id = module.project.project_id project_id = module.project.project_id
network = module.vpc.name network = module.vpc.name
custom_rules = { ingress_rules = {
allow-mesh = { allow-mesh = {
description = "Allow mesh" description = "Allow mesh"
direction = "INGRESS" priority = 900
action = "allow" source_ranges = ["10.1.0.0/16", "10.3.0.0/16"]
sources = [] targets = ["cluster-1-node", "cluster-2-node"]
ranges = ["10.1.0.0/16", "10.3.0.0/16"] },
targets = ["cluster-1-node", "cluster-2-node"]
use_service_accounts = false
rules = [{ protocol = "tcp", ports = null },
{ protocol = "udp", ports = null },
{ protocol = "icmp", ports = null },
{ protocol = "esp", ports = null },
{ protocol = "ah", ports = null },
{ protocol = "sctp", ports = null }]
extra_attributes = {
priority = 900
}
},
"allow-cluster-1-istio" = { "allow-cluster-1-istio" = {
description = "Allow istio sidecar injection, istioctl version and istioctl ps" description = "Allow istio sidecar injection, istioctl version and istioctl ps"
direction = "INGRESS" source_ranges = ["192.168.1.0/28"]
action = "allow" targets = ["cluster-1-node"]
sources = [] rules = [
ranges = [ "192.168.1.0/28" ] { protocol = "tcp", ports = [8080, 15014, 15017] }
targets = ["cluster-1-node"] ]
use_service_accounts = false
rules = [{ protocol = "tcp", ports = [8080, 15014, 15017] }]
extra_attributes = {
priority = 1000
}
}, },
"allow-cluster-2-istio" = { "allow-cluster-2-istio" = {
description = "Allow istio sidecar injection, istioctl version and istioctl ps" description = "Allow istio sidecar injection, istioctl version and istioctl ps"
direction = "INGRESS" source_ranges = ["192.168.2.0/28"]
action = "allow" targets = ["cluster-2-node"]
sources = [] rules = [
ranges = [ "192.168.2.0/28" ] { protocol = "tcp", ports = [8080, 15014, 15017] }
targets = ["cluster-2-node"] ]
use_service_accounts = false
rules = [{ protocol = "tcp", ports = [8080, 15014, 15017] }]
extra_attributes = {
priority = 1000
}
} }
} }
} }

View File

@ -2,11 +2,10 @@
This module allows creation and management of different types of firewall rules for a single VPC network: This module allows creation and management of different types of firewall rules for a single VPC network:
- blanket ingress rules based on IP ranges that allow all traffic via the `admin_ranges` variable - custom rules via the `egress_rules` and `ingress_rules` variables
- simplified tag-based ingress rules for the HTTP, HTTPS and SSH protocols via the `xxx_source_ranges` variables; HTTP and HTTPS tags match those set by the console via the "Allow HTTP(S) traffic" instance flags - optional predefined rules that simplify prototyping via the `default_rules_config` variable
- custom rules via the `custom_rules` variables
The simplified tag-based rules are enabled by default, set to the ranges of the GCP health checkers for HTTP/HTTPS, and the IAP forwarders for SSH. To disable them set the corresponding variables to empty lists. The predefined rules are enabled by default and set to the ranges of the GCP health checkers for HTTP/HTTPS, and the IAP forwarders for SSH. See the relevant section below on how to configure or disable them.
## Examples ## Examples
@ -16,10 +15,12 @@ This is often useful for prototyping or testing infrastructure, allowing open in
```hcl ```hcl
module "firewall" { module "firewall" {
source = "./fabric/modules/net-vpc-firewall" source = "./fabric/modules/net-vpc-firewall"
project_id = "my-project" project_id = "my-project"
network = "my-network" network = "my-network"
admin_ranges = ["10.0.0.0/8"] default_rules_config = {
admin_ranges = ["10.0.0.0/8"]
}
} }
# tftest modules=1 resources=4 # tftest modules=1 resources=4
``` ```
@ -28,68 +29,117 @@ module "firewall" {
This is an example of how to define custom rules, with a sample rule allowing open ingress for the NTP protocol to instances with the `ntp-svc` tag. This is an example of how to define custom rules, with a sample rule allowing open ingress for the NTP protocol to instances with the `ntp-svc` tag.
```hcl Some implicit defaults are used in the rules variable types and can be controlled by explicitly setting specific attributes:
module "firewall" {
source = "./fabric/modules/net-vpc-firewall"
project_id = "my-project"
network = "my-network"
admin_ranges = ["10.0.0.0/8"]
custom_rules = {
ntp-svc = {
description = "NTP service."
direction = "INGRESS"
action = "allow"
sources = []
ranges = ["0.0.0.0/0"]
targets = ["ntp-svc"]
use_service_accounts = false
rules = [{ protocol = "udp", ports = [123] }]
extra_attributes = {}
}
}
}
# tftest modules=1 resources=5
```
### No predefined rules - action is controlled via the `deny` attribute which defaults to `true` for egress and `false` for ingress
- priority defaults to `1000`
If you don't want any predefined rules set `admin_ranges`, `http_source_ranges`, `https_source_ranges` and `ssh_source_ranges` to an empty list. - destination ranges (for egress) and source ranges (for ingress) default to `["0.0.0.0/0"]` if not explicitly set
- rules default to all protocols if not set
```hcl ```hcl
module "firewall" { module "firewall" {
source = "./fabric/modules/net-vpc-firewall" source = "./fabric/modules/net-vpc-firewall"
project_id = "my-project" project_id = "my-project"
network = "my-network" network = "my-network"
admin_ranges = [] default_rules_config = {
http_source_ranges = [] admin_ranges = ["10.0.0.0/8"]
https_source_ranges = [] }
ssh_source_ranges = [] egress_rules = {
custom_rules = { # implicit `deny` action
allow-https = { allow-egress-rfc1918 = {
description = "Allow HTTPS from internal networks." description = "Allow egress to RFC 1918 ranges."
direction = "INGRESS" destination_ranges = [
action = "allow" "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"
sources = [] ]
ranges = ["rfc1918"] # implicit { protocol = "all" } rule
targets = [] }
use_service_accounts = false deny-egress-all = {
rules = [{ protocol = "tcp", ports = [443] }] description = "Block egress."
extra_attributes = {} # implicit ["0.0.0.0/0"] destination ranges
# implicit { protocol = "all" } rule
}
}
ingress_rules = {
# implicit `allow` action
allow-ingress-ntp = {
description = "Allow NTP service based on tag."
source_ranges = ["0.0.0.0/0"]
targets = ["ntp-svc"]
rules = [{ protocol = "udp", ports = [123] }]
} }
} }
} }
# tftest modules=1 resources=1 # tftest modules=1 resources=7
``` ```
### Controlling or turning off default rules
Predefined rules can be controlled or turned off via the `default_rules_config` variable.
#### Overriding default tags and ranges
Each protocol rule has a default set of tags and ranges:
- the health check range and the `http-server`/`https-server` tag for HTTP/HTTPS, matching tags set via GCP console flags on GCE instances
- the IAP forwarders range and `ssh` tag for SSH
Default tags and ranges can be overridden for each protocol, like shown here for SSH:
```hcl
module "firewall" {
source = "./fabric/modules/net-vpc-firewall"
project_id = "my-project"
network = "my-network"
default_rules_config = {
ssh_ranges = ["10.0.0.0/8"]
ssh_rags = ["ssh-default"]
}
}
# tftest modules=1 resources=3
```
#### Disabling predefined rules
Default rules can be disabled individually by specifying an empty set of ranges:
```hcl
module "firewall" {
source = "./fabric/modules/net-vpc-firewall"
project_id = "my-project"
network = "my-network"
default_rules_config = {
ssh_ranges = []
}
}
# tftest modules=1 resources=2
```
Or the entire set of rules can be disabled via the `disabled` attribute:
```hcl
module "firewall" {
source = "./fabric/modules/net-vpc-firewall"
project_id = "my-project"
network = "my-network"
default_rules_config = {
disabled = true
}
}
# tftest modules=0 resources=0
```
### Rules Factory ### Rules Factory
The module includes a rules factory (see [Resource Factories](../../blueprints/factories/)) for the massive creation of rules leveraging YaML configuration files. Each configuration file can optionally contain more than one rule which a structure that reflects the `custom_rules` variable. The module includes a rules factory (see [Resource Factories](../../blueprints/factories/)) for the massive creation of rules leveraging YaML configuration files. Each configuration file can optionally contain more than one rule which a structure that reflects the `custom_rules` variable.
```hcl ```hcl
module "firewall" { module "firewall" {
source = "./fabric/modules/net-vpc-firewall" source = "./fabric/modules/net-vpc-firewall"
project_id = "my-project" project_id = "my-project"
network = "my-network" network = "my-network"
factories_config = {
}
data_folder = "config/firewall" data_folder = "config/firewall"
cidr_template_file = "config/cidr_template.yaml" cidr_template_file = "config/cidr_template.yaml"
} }
@ -100,13 +150,9 @@ module "firewall" {
# ./config/firewall/load_balancers.yaml # ./config/firewall/load_balancers.yaml
allow-healthchecks: allow-healthchecks:
description: Allow ingress from healthchecks. description: Allow ingress from healthchecks.
direction: INGRESS
action: allow
sources: []
ranges: ranges:
- $healthchecks - healthchecks
targets: ["lb-backends"] targets: ["lb-backends"]
use_service_accounts: false
rules: rules:
- protocol: tcp - protocol: tcp
ports: ports:
@ -129,26 +175,19 @@ healthchecks:
| name | description | type | required | default | | name | description | type | required | default |
|---|---|:---:|:---:|:---:| |---|---|:---:|:---:|:---:|
| [network](variables.tf#L80) | Name of the network this set of firewall rules applies to. | <code>string</code> | ✓ | | | [network](variables.tf#L109) | Name of the network this set of firewall rules applies to. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L85) | Project id of the project that holds the network. | <code>string</code> | ✓ | | | [project_id](variables.tf#L114) | Project id of the project that holds the network. | <code>string</code> | ✓ | |
| [admin_ranges](variables.tf#L17) | IP CIDR ranges that have complete access to all subnets. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> | | [default_rules_config](variables.tf#L17) | Optionally created convenience rules. Set the variable or individual members to null to disable. | <code title="object&#40;&#123;&#10; admin_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; http_ranges &#61; optional&#40;list&#40;string&#41;, &#91;&#10; &#34;35.191.0.0&#47;16&#34;, &#34;130.211.0.0&#47;22&#34;, &#34;209.85.152.0&#47;22&#34;, &#34;209.85.204.0&#47;22&#34;&#93;&#10; &#41;&#10; http_tags &#61; optional&#40;list&#40;string&#41;, &#91;&#34;http-server&#34;&#93;&#41;&#10; https_ranges &#61; optional&#40;list&#40;string&#41;, &#91;&#10; &#34;35.191.0.0&#47;16&#34;, &#34;130.211.0.0&#47;22&#34;, &#34;209.85.152.0&#47;22&#34;, &#34;209.85.204.0&#47;22&#34;&#93;&#10; &#41;&#10; https_tags &#61; optional&#40;list&#40;string&#41;, &#91;&#34;https-server&#34;&#93;&#41;&#10; ssh_ranges &#61; optional&#40;list&#40;string&#41;, &#91;&#34;35.235.240.0&#47;20&#34;&#93;&#41;&#10; ssh_tags &#61; optional&#40;list&#40;string&#41;, &#91;&#34;ssh&#34;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [cidr_template_file](variables.tf#L23) | Path for optional file containing name->cidr_list map to be used by the rules factory. | <code>string</code> | | <code>null</code> | | [egress_rules](variables.tf#L37) | List of egress rule definitions, default to deny action. | <code title="map&#40;object&#40;&#123;&#10; deny &#61; optional&#40;bool, true&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; enable_logging &#61; optional&#40;object&#40;&#123;&#10; include_metadata &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; priority &#61; optional&#40;number, 1000&#41;&#10; sources &#61; optional&#40;list&#40;string&#41;&#41;&#10; targets &#61; optional&#40;list&#40;string&#41;&#41;&#10; use_service_accounts &#61; optional&#40;bool, false&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; protocol &#61; string&#10; ports &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#123; protocol &#61; &#34;all&#34; &#125;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [custom_rules](variables.tf#L29) | List of custom rule definitions (refer to variables file for syntax). | <code title="map&#40;object&#40;&#123;&#10; description &#61; string&#10; direction &#61; string&#10; action &#61; string &#35; &#40;allow&#124;deny&#41;&#10; ranges &#61; list&#40;string&#41;&#10; sources &#61; list&#40;string&#41;&#10; targets &#61; list&#40;string&#41;&#10; use_service_accounts &#61; bool&#10; rules &#61; list&#40;object&#40;&#123;&#10; protocol &#61; string&#10; ports &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; extra_attributes &#61; map&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [factories_config](variables.tf#L83) | Paths to data files and folders that enable factory functionality. | <code title="object&#40;&#123;&#10; cidr_tpl_file &#61; optional&#40;string&#41;&#10; rules_folder &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [data_folder](variables.tf#L48) | Path for optional folder containing firewall rules defined as YaML objects used by the rules factory. | <code>string</code> | | <code>null</code> | | [ingress_rules](variables.tf#L60) | List of ingress rule definitions, default to allow action. | <code title="map&#40;object&#40;&#123;&#10; deny &#61; optional&#40;bool, false&#41;&#10; description &#61; optional&#40;string&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; enable_logging &#61; optional&#40;object&#40;&#123;&#10; include_metadata &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; priority &#61; optional&#40;number, 1000&#41;&#10; source_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; sources &#61; optional&#40;list&#40;string&#41;&#41;&#10; targets &#61; optional&#40;list&#40;string&#41;&#41;&#10; use_service_accounts &#61; optional&#40;bool, false&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; protocol &#61; string&#10; ports &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#123; protocol &#61; &#34;all&#34; &#125;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [http_source_ranges](variables.tf#L54) | List of IP CIDR ranges for tag-based HTTP rule, defaults to the health checkers ranges. | <code>list&#40;string&#41;</code> | | <code>&#91;&#34;35.191.0.0&#47;16&#34;, &#34;130.211.0.0&#47;22&#34;, &#34;209.85.152.0&#47;22&#34;, &#34;209.85.204.0&#47;22&#34;&#93;</code> | | [named_ranges](variables.tf#L92) | Define mapping of names to ranges that can be used in custom rules. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; any &#61; &#91;&#34;0.0.0.0&#47;0&#34;&#93;&#10; dns-forwarders &#61; &#91;&#34;35.199.192.0&#47;19&#34;&#93;&#10; health-checkers &#61; &#91;&#10; &#34;35.191.0.0&#47;16&#34;, &#34;130.211.0.0&#47;22&#34;, &#34;209.85.152.0&#47;22&#34;, &#34;209.85.204.0&#47;22&#34;&#10; &#93;&#10; iap-forwarders &#61; &#91;&#34;35.235.240.0&#47;20&#34;&#93;&#10; private-googleapis &#61; &#91;&#34;199.36.153.8&#47;30&#34;&#93;&#10; restricted-googleapis &#61; &#91;&#34;199.36.153.4&#47;30&#34;&#93;&#10; rfc1918 &#61; &#91;&#34;10.0.0.0&#47;8&#34;, &#34;172.16.0.0&#47;12&#34;, &#34;192.168.0.0&#47;16&#34;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [https_source_ranges](variables.tf#L60) | List of IP CIDR ranges for tag-based HTTPS rule, defaults to the health checkers ranges. | <code>list&#40;string&#41;</code> | | <code>&#91;&#34;35.191.0.0&#47;16&#34;, &#34;130.211.0.0&#47;22&#34;, &#34;209.85.152.0&#47;22&#34;, &#34;209.85.204.0&#47;22&#34;&#93;</code> |
| [named_ranges](variables.tf#L66) | Names that can be used of valid values for the `ranges` field of `custom_rules`. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; any &#61; &#91;&#34;0.0.0.0&#47;0&#34;&#93;&#10; dns-forwarders &#61; &#91;&#34;35.199.192.0&#47;19&#34;&#93;&#10; health-checkers &#61; &#91;&#34;35.191.0.0&#47;16&#34;, &#34;130.211.0.0&#47;22&#34;, &#34;209.85.152.0&#47;22&#34;, &#34;209.85.204.0&#47;22&#34;&#93;&#10; iap-forwarders &#61; &#91;&#34;35.235.240.0&#47;20&#34;&#93;&#10; private-googleapis &#61; &#91;&#34;199.36.153.8&#47;30&#34;&#93;&#10; restricted-googleapis &#61; &#91;&#34;199.36.153.4&#47;30&#34;&#93;&#10; rfc1918 &#61; &#91;&#34;10.0.0.0&#47;8&#34;, &#34;172.16.0.0&#47;12&#34;, &#34;192.168.0.0&#47;16&#34;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [ssh_source_ranges](variables.tf#L90) | List of IP CIDR ranges for tag-based SSH rule, defaults to the IAP forwarders range. | <code>list&#40;string&#41;</code> | | <code>&#91;&#34;35.235.240.0&#47;20&#34;&#93;</code> |
## Outputs ## Outputs
| name | description | sensitive | | name | description | sensitive |
|---|---|:---:| |---|---|:---:|
| [admin_ranges](outputs.tf#L17) | Admin ranges data. | | | [default_rules](outputs.tf#L17) | Default rule resources. | |
| [custom_egress_allow_rules](outputs.tf#L26) | Custom egress rules with allow blocks. | | | [rules](outputs.tf#L27) | Custom rule resources. | |
| [custom_egress_deny_rules](outputs.tf#L34) | Custom egress rules with allow blocks. | |
| [custom_ingress_allow_rules](outputs.tf#L42) | Custom ingress rules with allow blocks. | |
| [custom_ingress_deny_rules](outputs.tf#L50) | Custom ingress rules with deny blocks. | |
| [rules](outputs.tf#L58) | All google_compute_firewall resources created. | |
<!-- END TFDOC --> <!-- END TFDOC -->

View File

@ -0,0 +1,77 @@
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
# tfdoc:file:description Optional default rule resources.
locals {
default_rules = {
for k, v in var.default_rules_config :
k => var.default_rules_config.disabled == true || v == null ? [] : v
if k != "disabled"
}
}
resource "google_compute_firewall" "allow-admins" {
count = length(local.default_rules.admin_ranges) > 0 ? 1 : 0
name = "${var.network}-ingress-admins"
description = "Access from the admin subnet to all subnets."
network = var.network
project = var.project_id
source_ranges = local.default_rules.admin_ranges
allow { protocol = "all" }
}
resource "google_compute_firewall" "allow-tag-http" {
count = length(local.default_rules.http_ranges) > 0 ? 1 : 0
name = "${var.network}-ingress-tag-http"
description = "Allow http to machines with matching tags."
network = var.network
project = var.project_id
source_ranges = local.default_rules.http_ranges
target_tags = local.default_rules.http_tags
allow {
protocol = "tcp"
ports = ["80"]
}
}
resource "google_compute_firewall" "allow-tag-https" {
count = length(local.default_rules.https_ranges) > 0 ? 1 : 0
name = "${var.network}-ingress-tag-https"
description = "Allow http to machines with matching tags."
network = var.network
project = var.project_id
source_ranges = local.default_rules.https_ranges
target_tags = local.default_rules.https_tags
allow {
protocol = "tcp"
ports = ["443"]
}
}
resource "google_compute_firewall" "allow-tag-ssh" {
count = length(local.default_rules.ssh_ranges) > 0 ? 1 : 0
name = "${var.network}-ingress-tag-ssh"
description = "Allow SSH to machines with matching tags."
network = var.network
project = var.project_id
source_ranges = local.default_rules.ssh_ranges
target_tags = local.default_rules.ssh_tags
allow {
protocol = "tcp"
ports = ["22"]
}
}

View File

@ -15,149 +15,125 @@
*/ */
locals { locals {
_custom_rules = { # define list of rule files
for id, rule in var.custom_rules : _factory_rule_files = [
id => merge(rule, { for f in try(fileset(var.factories_config.rules_folder, "**/*.yaml"), []) :
# make rules a map so we use it in a for_each "${var.factories_config.rules_folder}/${f}"
rules = { for index, ports in rule.rules : index => ports } ]
# lookup any named ranges references # decode rule files and account for optional attributes
ranges = flatten([ _factory_rule_list = flatten([
for range in rule.ranges : for f in local._factory_rule_files : [
try(var.named_ranges[range], range) for direction, ruleset in yamldecode(file(f)) : [
for name, rule in ruleset : {
name = name
deny = try(rule.deny, false)
rules = try(rule.rules, [{ protocol = "all" }])
description = try(rule.description, null)
destination_ranges = try(rule.destination_ranges, null)
direction = upper(direction)
disabled = try(rule.disabled, null)
enable_logging = try(rule.enable_logging, null)
priority = try(rule.priority, 1000)
source_ranges = try(rule.source_ranges, null)
sources = try(rule.sources, null)
targets = try(rule.targets, null)
use_service_accounts = try(rule.use_service_accounts, false)
}
]
]
])
_factory_rules = {
for r in local._factory_rule_list : r.name => r
if contains(["EGRESS", "INGRESS"], r.direction)
}
_named_ranges = merge(
try(yamldecode(file(var.factories_config.cidr_tpl_file)), {}),
var.named_ranges
)
_rules = merge(
local._factory_rules, local._rules_egress, local._rules_ingress
)
_rules_egress = {
for name, rule in merge(var.egress_rules) :
name => merge(rule, { direction = "EGRESS" })
}
_rules_ingress = {
for name, rule in merge(var.ingress_rules) :
name => merge(rule, { direction = "INGRESS" })
}
# convert rules data to resource format and replace range template variables
rules = {
for name, rule in local._rules :
name => merge(rule, {
action = rule.deny == true ? "DENY" : "ALLOW"
destination_ranges = flatten([
for range in coalesce(try(rule.destination_ranges, null), []) :
try(local._named_ranges[range], range)
])
rules = { for k, v in rule.rules : k => v }
source_ranges = flatten([
for range in coalesce(try(rule.source_ranges, null), []) :
try(local._named_ranges[range], range)
]) ])
}) })
} }
cidrs = try({
for name, cidrs in yamldecode(file(var.cidr_template_file)) :
name => cidrs
}, {})
_factory_rules_raw = flatten([
for file in try(fileset(var.data_folder, "**/*.yaml"), []) : [
for key, ruleset in yamldecode(file("${var.data_folder}/${file}")) :
merge(ruleset, {
name = "${key}"
rules = { for index, ports in ruleset.rules : index => ports }
ranges = try(ruleset.ranges, null) == null ? null : flatten(
[for cidr in ruleset.ranges :
can(regex("^\\$", cidr))
? local.cidrs[trimprefix(cidr, "$")]
: [cidr]
])
extra_attributes = try(ruleset.extra_attributes, {})
})
]
])
_factory_rules = {
for d in local._factory_rules_raw : d["name"] => d
}
custom_rules = merge(local._custom_rules, local._factory_rules)
} }
###############################################################################
# rules based on IP ranges
###############################################################################
resource "google_compute_firewall" "allow-admins" {
count = length(var.admin_ranges) > 0 ? 1 : 0
name = "${var.network}-ingress-admins"
description = "Access from the admin subnet to all subnets"
network = var.network
project = var.project_id
source_ranges = var.admin_ranges
allow { protocol = "all" }
}
###############################################################################
# rules based on tags
###############################################################################
resource "google_compute_firewall" "allow-tag-ssh" {
count = length(var.ssh_source_ranges) > 0 ? 1 : 0
name = "${var.network}-ingress-tag-ssh"
description = "Allow SSH to machines with the 'ssh' tag"
network = var.network
project = var.project_id
source_ranges = var.ssh_source_ranges
target_tags = ["ssh"]
allow {
protocol = "tcp"
ports = ["22"]
}
}
resource "google_compute_firewall" "allow-tag-http" {
count = length(var.http_source_ranges) > 0 ? 1 : 0
name = "${var.network}-ingress-tag-http"
description = "Allow HTTP to machines with the 'http-server' tag"
network = var.network
project = var.project_id
source_ranges = var.http_source_ranges
target_tags = ["http-server"]
allow {
protocol = "tcp"
ports = ["80"]
}
}
resource "google_compute_firewall" "allow-tag-https" {
count = length(var.https_source_ranges) > 0 ? 1 : 0
name = "${var.network}-ingress-tag-https"
description = "Allow HTTPS to machines with the 'https' tag"
network = var.network
project = var.project_id
source_ranges = var.https_source_ranges
target_tags = ["https-server"]
allow {
protocol = "tcp"
ports = ["443"]
}
}
################################################################################
# dynamic rules #
################################################################################
resource "google_compute_firewall" "custom-rules" { resource "google_compute_firewall" "custom-rules" {
# provider = "google-beta" for_each = local.rules
for_each = local.custom_rules project = var.project_id
network = var.network
name = each.key name = each.key
description = each.value.description description = each.value.description
direction = each.value.direction direction = each.value.direction
network = var.network
project = var.project_id
source_ranges = ( source_ranges = (
each.value.direction == "INGRESS" each.value.direction == "INGRESS"
? coalesce(each.value.ranges, []) == [] ? ["0.0.0.0/0"] : each.value.ranges ? (
: null coalesce(each.value.source_ranges, []) == []
? ["0.0.0.0/0"]
: each.value.source_ranges
) : null
) )
destination_ranges = ( destination_ranges = (
each.value.direction == "EGRESS" each.value.direction == "EGRESS"
? coalesce(each.value.ranges, []) == [] ? ["0.0.0.0/0"] : each.value.ranges ? (
coalesce(each.value.destination_ranges, []) == []
? ["0.0.0.0/0"]
: each.value.destination_ranges
) : null
)
source_tags = (
each.value.use_service_accounts || each.value.direction == "EGRESS"
? null
: each.value.sources
)
source_service_accounts = (
each.value.use_service_accounts && each.value.direction == "INGRESS"
? each.value.sources
: null : null
) )
source_tags = each.value.use_service_accounts || each.value.direction == "EGRESS" ? null : each.value.sources target_tags = (
source_service_accounts = each.value.use_service_accounts && each.value.direction == "INGRESS" ? each.value.sources : null each.value.use_service_accounts ? null : each.value.targets
target_tags = each.value.use_service_accounts ? null : each.value.targets )
target_service_accounts = each.value.use_service_accounts ? each.value.targets : null target_service_accounts = (
disabled = lookup(each.value.extra_attributes, "disabled", false) each.value.use_service_accounts ? each.value.targets : null
priority = lookup(each.value.extra_attributes, "priority", 1000) )
disabled = each.value.disabled == true
priority = each.value.priority
dynamic "log_config" { dynamic "log_config" {
for_each = lookup(each.value.extra_attributes, "logging", null) != null ? [each.value.extra_attributes.logging] : [] for_each = each.value.enable_logging == null ? [] : [""]
iterator = logging_config
content { content {
metadata = logging_config.value metadata = (
try(each.value.enable_logging.include_metadata, null) == true
? "INCLUDE_ALL_METADATA"
: "EXCLUDE_ALL_METADATA"
)
} }
} }
dynamic "deny" { dynamic "deny" {
for_each = each.value.action == "deny" ? each.value.rules : {} for_each = each.value.action == "DENY" ? each.value.rules : {}
iterator = rule iterator = rule
content { content {
protocol = rule.value.protocol protocol = rule.value.protocol
@ -166,8 +142,7 @@ resource "google_compute_firewall" "custom-rules" {
} }
dynamic "allow" { dynamic "allow" {
for_each = each.value.action == "allow" ? each.value.rules : {} for_each = each.value.action == "ALLOW" ? each.value.rules : {}
iterator = rule iterator = rule
content { content {
protocol = rule.value.protocol protocol = rule.value.protocol

View File

@ -14,54 +14,17 @@
* limitations under the License. * limitations under the License.
*/ */
output "admin_ranges" { output "default_rules" {
description = "Admin ranges data." description = "Default rule resources."
value = { value = {
enabled = length(var.admin_ranges) > 0 admin = try(google_compute_firewall.allow-admins, null)
ranges = join(",", var.admin_ranges) http = try(google_compute_firewall.allow-tag-http, null)
https = try(google_compute_firewall.allow-tag-https, null)
ssh = try(google_compute_firewall.allow-tag-ssh, null)
} }
} }
output "custom_egress_allow_rules" {
description = "Custom egress rules with allow blocks."
value = [
for rule in google_compute_firewall.custom-rules :
rule.name if rule.direction == "EGRESS" && try(length(rule.allow), 0) > 0
]
}
output "custom_egress_deny_rules" {
description = "Custom egress rules with allow blocks."
value = [
for rule in google_compute_firewall.custom-rules :
rule.name if rule.direction == "EGRESS" && try(length(rule.deny), 0) > 0
]
}
output "custom_ingress_allow_rules" {
description = "Custom ingress rules with allow blocks."
value = [
for rule in google_compute_firewall.custom-rules :
rule.name if rule.direction == "INGRESS" && try(length(rule.allow), 0) > 0
]
}
output "custom_ingress_deny_rules" {
description = "Custom ingress rules with deny blocks."
value = [
for rule in google_compute_firewall.custom-rules :
rule.name if rule.direction == "INGRESS" && try(length(rule.deny), 0) > 0
]
}
output "rules" { output "rules" {
description = "All google_compute_firewall resources created." description = "Custom rule resources."
value = merge( value = google_compute_firewall.custom-rules
google_compute_firewall.custom-rules,
try({ (google_compute_firewall.allow-admins.0.name) = google_compute_firewall.allow-admins.0 }, {}),
try({ (google_compute_firewall.allow-tag-ssh.0.name) = google_compute_firewall.allow-tag-ssh.0 }, {}),
try({ (google_compute_firewall.allow-tag-http.0.name) = google_compute_firewall.allow-tag-http.0 }, {}),
try({ (google_compute_firewall.allow-tag-https.0.name) = google_compute_firewall.allow-tag-https.0 }, {})
)
} }

View File

@ -14,67 +14,96 @@
* limitations under the License. * limitations under the License.
*/ */
variable "admin_ranges" { variable "default_rules_config" {
description = "IP CIDR ranges that have complete access to all subnets." description = "Optionally created convenience rules. Set the variable or individual members to null to disable."
type = list(string) type = object({
default = [] admin_ranges = optional(list(string))
disabled = optional(bool, false)
http_ranges = optional(list(string), [
"35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"]
)
http_tags = optional(list(string), ["http-server"])
https_ranges = optional(list(string), [
"35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"]
)
https_tags = optional(list(string), ["https-server"])
ssh_ranges = optional(list(string), ["35.235.240.0/20"])
ssh_tags = optional(list(string), ["ssh"])
})
default = {}
nullable = false
} }
variable "cidr_template_file" { variable "egress_rules" {
description = "Path for optional file containing name->cidr_list map to be used by the rules factory." description = "List of egress rule definitions, default to deny action."
type = string
default = null
}
variable "custom_rules" {
description = "List of custom rule definitions (refer to variables file for syntax)."
type = map(object({ type = map(object({
description = string deny = optional(bool, true)
direction = string description = optional(string)
action = string # (allow|deny) destination_ranges = optional(list(string))
ranges = list(string) disabled = optional(bool, false)
sources = list(string) enable_logging = optional(object({
targets = list(string) include_metadata = optional(bool)
use_service_accounts = bool
rules = list(object({
protocol = string
ports = list(string)
})) }))
extra_attributes = map(string) priority = optional(number, 1000)
sources = optional(list(string))
targets = optional(list(string))
use_service_accounts = optional(bool, false)
rules = optional(list(object({
protocol = string
ports = optional(list(string))
})), [{ protocol = "all" }])
})) }))
default = {} default = {}
nullable = false
} }
variable "data_folder" { variable "ingress_rules" {
description = "Path for optional folder containing firewall rules defined as YaML objects used by the rules factory." description = "List of ingress rule definitions, default to allow action."
type = string type = map(object({
default = null deny = optional(bool, false)
description = optional(string)
disabled = optional(bool, false)
enable_logging = optional(object({
include_metadata = optional(bool)
}))
priority = optional(number, 1000)
source_ranges = optional(list(string))
sources = optional(list(string))
targets = optional(list(string))
use_service_accounts = optional(bool, false)
rules = optional(list(object({
protocol = string
ports = optional(list(string))
})), [{ protocol = "all" }])
}))
default = {}
nullable = false
} }
variable "http_source_ranges" { variable "factories_config" {
description = "List of IP CIDR ranges for tag-based HTTP rule, defaults to the health checkers ranges." description = "Paths to data files and folders that enable factory functionality."
type = list(string) type = object({
default = ["35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"] cidr_tpl_file = optional(string)
} rules_folder = string
})
variable "https_source_ranges" { default = null
description = "List of IP CIDR ranges for tag-based HTTPS rule, defaults to the health checkers ranges."
type = list(string)
default = ["35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"]
} }
variable "named_ranges" { variable "named_ranges" {
description = "Names that can be used of valid values for the `ranges` field of `custom_rules`." description = "Define mapping of names to ranges that can be used in custom rules."
type = map(list(string)) type = map(list(string))
default = { default = {
any = ["0.0.0.0/0"] any = ["0.0.0.0/0"]
dns-forwarders = ["35.199.192.0/19"] dns-forwarders = ["35.199.192.0/19"]
health-checkers = ["35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"] health-checkers = [
"35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"
]
iap-forwarders = ["35.235.240.0/20"] iap-forwarders = ["35.235.240.0/20"]
private-googleapis = ["199.36.153.8/30"] private-googleapis = ["199.36.153.8/30"]
restricted-googleapis = ["199.36.153.4/30"] restricted-googleapis = ["199.36.153.4/30"]
rfc1918 = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] rfc1918 = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
} }
nullable = false
} }
variable "network" { variable "network" {
@ -86,10 +115,3 @@ variable "project_id" {
description = "Project id of the project that holds the network." description = "Project id of the project that holds the network."
type = string type = string
} }
variable "ssh_source_ranges" {
description = "List of IP CIDR ranges for tag-based SSH rule, defaults to the IAP forwarders range."
type = list(string)
default = ["35.235.240.0/20"]
}

View File

@ -14,12 +14,11 @@
import os import os
FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture')
def test_resources(e2e_plan_runner): def test_resources(e2e_plan_runner):
"Test that plan works and the numbers of resources is as expected." "Test that plan works and the numbers of resources is as expected."
modules, resources = e2e_plan_runner(FIXTURES_DIR) modules, resources = e2e_plan_runner(FIXTURES_DIR)
assert len(modules) == 4 assert len(modules) == 4
assert len(resources) == 18 assert len(resources) == 20

View File

@ -28,8 +28,8 @@ BASEDIR = os.path.dirname(os.path.dirname(__file__))
def _plan_runner(): def _plan_runner():
"Returns a function to run Terraform plan on a fixture." "Returns a function to run Terraform plan on a fixture."
def run_plan(fixture_path=None, targets=None, refresh=True, tmpdir=True, def run_plan(fixture_path=None, extra_files=None, tf_var_file=None,
**tf_vars): targets=None, refresh=True, tmpdir=True, **tf_vars):
"Runs Terraform plan and returns parsed output." "Runs Terraform plan and returns parsed output."
if fixture_path is None: if fixture_path is None:
# find out the fixture directory from the caller's directory # find out the fixture directory from the caller's directory
@ -46,9 +46,9 @@ def _plan_runner():
shutil.copytree(fixture_path, tmp_path, dirs_exist_ok=True) shutil.copytree(fixture_path, tmp_path, dirs_exist_ok=True)
tf = tftest.TerraformTest(tmp_path if tmpdir else fixture_path, BASEDIR, tf = tftest.TerraformTest(tmp_path if tmpdir else fixture_path, BASEDIR,
os.environ.get('TERRAFORM', 'terraform')) os.environ.get('TERRAFORM', 'terraform'))
tf.setup(upgrade=True) tf.setup(extra_files=extra_files, upgrade=True)
plan = tf.plan(output=True, refresh=refresh, tf_vars=tf_vars, plan = tf.plan(output=True, refresh=refresh, tf_var_file=tf_var_file,
targets=targets) tf_vars=tf_vars, targets=targets)
return plan return plan
return run_plan return run_plan
@ -58,9 +58,11 @@ def _plan_runner():
def plan_runner(_plan_runner): def plan_runner(_plan_runner):
"Returns a function to run Terraform plan on a module fixture." "Returns a function to run Terraform plan on a module fixture."
def run_plan(fixture_path=None, targets=None, **tf_vars): def run_plan(fixture_path=None, extra_files=None, tf_var_file=None,
targets=None, **tf_vars):
"Runs Terraform plan and returns plan and module resources." "Runs Terraform plan and returns plan and module resources."
plan = _plan_runner(fixture_path, targets=targets, **tf_vars) plan = _plan_runner(fixture_path, extra_files=extra_files,
tf_var_file=tf_var_file, targets=targets, **tf_vars)
# skip the fixture # skip the fixture
root_module = plan.root_module['child_modules'][0] root_module = plan.root_module['child_modules'][0]
return plan, root_module['resources'] return plan, root_module['resources']

View File

@ -12,17 +12,14 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
allow-healthchecks: ingress:
description: Allow ingress from healthchecks. allow-healthchecks:
direction: INGRESS description: Allow ingress from healthchecks.
action: allow source_ranges:
sources: [] - healthchecks
ranges: targets: ["lb-backends"]
- $healthchecks rules:
targets: ["lb-backends"] - protocol: tcp
use_service_accounts: false ports:
rules: - 80
- protocol: tcp - 443
ports:
- 80
- 443

View File

@ -15,14 +15,11 @@
*/ */
module "firewall" { module "firewall" {
source = "../../../../modules/net-vpc-firewall" source = "../../../../modules/net-vpc-firewall"
project_id = var.project_id project_id = "test-project"
network = var.network network = "test-vpc"
admin_ranges = var.admin_ranges default_rules_config = var.default_rules_config
http_source_ranges = var.http_source_ranges egress_rules = var.egress_rules
https_source_ranges = var.https_source_ranges ingress_rules = var.ingress_rules
ssh_source_ranges = var.ssh_source_ranges factories_config = var.factories_config
custom_rules = var.custom_rules
data_folder = var.data_folder
cidr_template_file = var.cidr_template_file
} }

View File

@ -0,0 +1,22 @@
egress_rules = {
allow-egress-rfc1918 = {
description = "Allow egress to RFC 1918 ranges."
is_egress = true
destination_ranges = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
}
deny-egress-all = {
description = "Block egress."
is_deny = true
is_egress = true
}
}
ingress_rules = {
allow-ingress-ntp = {
description = "Allow NTP service based on tag."
targets = ["ntp-svc"]
rules = [{ protocol = "udp", ports = [123] }]
}
}
default_rules_config = {
disabled = true
}

View File

@ -14,84 +14,38 @@
* limitations under the License. * limitations under the License.
*/ */
variable "admin_ranges" { /**
description = "IP CIDR ranges that have complete access to all subnets." * Copyright 2022 Google LLC
type = list(string) *
default = [] * Licensed under the Apache License, Version 2.0 (the "License");
} * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
variable "cidr_template_file" { variable "default_rules_config" {
description = "Path for optional file containing name->cidr_list map to be used by the rules factory." type = any
type = string
default = null
}
variable "custom_rules" {
description = "List of custom rule definitions (refer to variables file for syntax)."
type = map(object({
description = string
direction = string
action = string # (allow|deny)
ranges = list(string)
sources = list(string)
targets = list(string)
use_service_accounts = bool
rules = list(object({
protocol = string
ports = list(string)
}))
extra_attributes = map(string)
}))
default = {} default = {}
} }
variable "data_folder" { variable "egress_rules" {
description = "Path for optional folder containing firewall rules defined as YaML objects used by the rules factory." type = any
type = string default = {}
default = null
} }
variable "http_source_ranges" { variable "factories_config" {
description = "List of IP CIDR ranges for tag-based HTTP rule, defaults to the health checkers ranges." type = any
type = list(string) default = null
default = ["35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"]
} }
variable "https_source_ranges" { variable "ingress_rules" {
description = "List of IP CIDR ranges for tag-based HTTPS rule, defaults to the health checkers ranges." type = any
type = list(string) default = {}
default = ["35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"]
} }
variable "named_ranges" {
description = "Names that can be used of valid values for the `ranges` field of `custom_rules`."
type = map(list(string))
default = {
any = ["0.0.0.0/0"]
dns-forwarders = ["35.199.192.0/19"]
health-checkers = ["35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"]
iap-forwarders = ["35.235.240.0/20"]
private-googleapis = ["199.36.153.8/30"]
restricted-googleapis = ["199.36.153.4/30"]
rfc1918 = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
}
}
variable "network" {
description = "Name of the network this set of firewall rules applies to."
type = string
default = "vpc"
}
variable "project_id" {
description = "Project id of the project that holds the network."
type = string
default = "project"
}
variable "ssh_source_ranges" {
description = "List of IP CIDR ranges for tag-based SSH rule, defaults to the IAP forwarders range."
type = list(string)
default = ["35.235.240.0/20"]
}

View File

@ -12,27 +12,48 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
def test_vpc_firewall_simple(plan_runner): import pytest
"Test vpc with no extra options."
def test_defaults(plan_runner):
"Test variable defaults."
_, resources = plan_runner() _, resources = plan_runner()
assert len(resources) == 3 assert len(resources) == 3
assert set([r['type'] for r in resources]) == set( assert set([r['type'] for r in resources]) == set(['google_compute_firewall'])
['google_compute_firewall']) assert set([r['values']['name'] for r in resources]) == set([
assert set([r['values']['name'] for r in resources]) == set( 'test-vpc-ingress-tag-http', 'test-vpc-ingress-tag-https',
['vpc-ingress-tag-http', 'vpc-ingress-tag-https', 'vpc-ingress-tag-ssh']) 'test-vpc-ingress-tag-ssh'
assert set([r['values']['project'] for r in resources]) == set(['project']) ])
assert set([r['values']['network'] for r in resources]) == set(['vpc']) assert set([r['values']['project'] for r in resources
]) == set(['test-project'])
assert set([r['values']['network'] for r in resources]) == set(['test-vpc'])
def test_vpc_firewall_factory(plan_runner): def test_rules(plan_runner):
"Test shared vpc variables." "Test custom rules."
_, resources = plan_runner( tfvars = 'test.rules.tfvars'
data_folder="config/firewall", _, resources = plan_runner(extra_files=[tfvars], tf_var_file=tfvars)
cidr_template_file="config/cidr_template.yaml" assert len(resources) == 3
) rules = {r['index']: r['values'] for r in resources}
rule = rules['allow-ingress-ntp']
assert rule['source_ranges'] == ['0.0.0.0/0']
assert rule['allow'] == [{'ports': ['123'], 'protocol': 'udp'}]
rule = rules['deny-egress-all']
assert rule['destination_ranges'] == ['0.0.0.0/0']
assert rule['deny'] == [{'ports': [], 'protocol': 'all'}]
def test_factory(plan_runner):
"Test factory."
factories_config = '''{
cidr_tpl_file = "config/cidr_template.yaml"
rules_folder = "config/firewall"
}'''
_, resources = plan_runner(factories_config=factories_config)
assert len(resources) == 4 assert len(resources) == 4
factory_rule = [r for r in resources if r["values"] factory_rule = [
["name"] == "allow-healthchecks"][0]["values"] r for r in resources if r["values"]["name"] == "allow-healthchecks"
][0]["values"]
assert set(factory_rule["source_ranges"]) == set( assert set(factory_rule["source_ranges"]) == set(
["130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22", "35.191.0.0/16"]) ["130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22", "35.191.0.0/16"])
assert set(factory_rule["target_tags"]) == set(["lb-backends"]) assert set(factory_rule["target_tags"]) == set(["lb-backends"])