Added example with GLB and Cloud Armor

This commit is contained in:
Miren Esnaola 2022-04-11 10:52:51 +02:00
parent 22d0506f4d
commit 9f5d59748d
8 changed files with 525 additions and 0 deletions

View File

@ -0,0 +1,64 @@
# HTTP Load Balancer with Cloud Armor
Google Cloud HTTP(S) load balancing is implemented at the edge of Google's network in Google's points of presence (POP) around the world. User traffic directed to an HTTP(S) load balancer enters the POP closest to the user and is then load balanced over Google's global network to the closest backend that has sufficient capacity available.
Cloud Armor IP allowlist/denylist enable you to restrict or allow access to your HTTP(S) load balancer at the edge of the Google Cloud, as close as possible to the user and to malicious traffic. This prevents malicious users or traffic from consuming resources or entering your virtual private cloud (VPC) networks.
In this lab, you configure an HTTP Load Balancer with global backends, as shown in the diagram below. Then, you stress test the Load Balancer and denylist the stress test IP with Cloud Armor.
![Architecture](architecture.png)
## Running the example
Clone this repository or [open it in cloud shell](https://ssh.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https%3A%2F%2Fgithub.com%2Fterraform-google-modules%2Fcloud-foundation-fabric&cloudshell_print=cloud-shell-readme.txt&cloudshell_working_dir=examples%2Fcloud-operations%2Fglb-and-armor), then go through the following steps to create resources:
* `terraform init`
* `terraform apply -var project_id=my-project-id`
The following outputs will be available once everything is deployed:
* `glb_ip_address`, containing the IPv4 address of the HTTP Load Balancer
* `vm_siege_external_ip`, containing the external IPv4 address of the siege VM.
Once done testing, you can clean up resources by running `terraform destroy`.
## Testing the example
1. Connect to the siege VM and run the following command
siege -c 250 -t150s http://$LB_IP`ß
2. In the Cloud Console, on the Navigation menu, click Network Services > Load balancing.
3. Click Backends.
4. Click http-backend.
5. Navigate to http-lb.
6. Click on the Monitoring tab.
7. Monitor the Frontend Location (Total inbound traffic) between North America and the two backends for 2 to 3 minutes. At first, traffic should just be directed to us-east1-mig but as the RPS increases, traffic is also directed to europe-west1-mig. This demonstrates that by default traffic is forwarded to the closest backend but if the load is very high, traffic can be distributed across the backends.
8. Re-run terraform as follows:
terraform apply -var project_id=my-project-id -var enforce_security_policy=true
Like this we have applied a security policy to denylist the IP address of the siege VM
9. From the siege VM run the following command and verify that you get a 403 Forbidden error code back.
curl http://$LB_IP
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [project_id](variables.tf#L26) | Identifier of the project. | <code>string</code> | ✓ | |
| [enforce_security_policy](variables.tf#L31) | Enforce security policy. | <code>bool</code> | | <code>true</code> |
| [prefix](variables.tf#L37) | Prefix used for created resources. | <code>string</code> | | <code>null</code> |
| [project_create](variables.tf#L17) | Parameters for the creation of the new project. | <code title="object&#40;&#123;&#10; billing_account_id &#61; string&#10; parent &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [glb_ip_address](outputs.tf#L18) | Load balancer IP address. | |
| [vm_siege_external_ip](outputs.tf#L23) | Siege VM external IP address. | |
<!-- END TFDOC -->

View File

@ -0,0 +1,308 @@
/**
* 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.
*/
locals {
prefix = (var.prefix == null || var.prefix == "") ? "" : "${var.prefix}-"
}
module "project" {
source = "../../../modules/project"
billing_account = (var.project_create != null
? var.project_create.billing_account_id
: null
)
parent = (var.project_create != null
? var.project_create.parent
: null
)
prefix = var.project_create == null ? null : var.prefix
name = var.project_id
services = [
"compute.googleapis.com"
]
service_config = {
disable_on_destroy = false
disable_dependent_services = false
}
project_create = var.project_create != null
}
module "vpc" {
source = "../../../modules/net-vpc"
project_id = module.project.project_id
name = "${local.prefix}vpc"
subnets = [
{
ip_cidr_range = "10.0.1.0/24"
name = "subnet-ew1"
region = "europe-west1"
secondary_ip_range = null
},
{
ip_cidr_range = "10.0.2.0/24"
name = "subnet-ue1"
region = "us-east1"
secondary_ip_range = null
},
{
ip_cidr_range = "10.0.3.0/24"
name = "subnet-uw1"
region = "us-west1"
secondary_ip_range = null
}
]
}
module "firewall" {
source = "../../../modules/net-vpc-firewall"
project_id = module.project.project_id
network = module.vpc.name
}
module "nat_ew1" {
source = "../../../modules/net-cloudnat"
project_id = module.project.project_id
region = "europe-west1"
name = "${local.prefix}nat-eu1"
router_network = module.vpc.name
}
module "nat_ue1" {
source = "../../../modules/net-cloudnat"
project_id = module.project.project_id
region = "us-east1"
name = "${local.prefix}nat-ue1"
router_network = module.vpc.name
}
module "instance_template_ew1" {
source = "../../../modules/compute-vm"
project_id = module.project.project_id
zone = "europe-west1-b"
name = "${local.prefix}europe-west1-template"
instance_type = "n1-standard-2"
network_interfaces = [{
network = module.vpc.self_link
subnetwork = module.vpc.subnet_self_links["europe-west1/subnet-ew1"]
nat = false
addresses = null
}]
boot_disk = {
image = "projects/debian-cloud/global/images/family/debian-11"
type = "pd-ssd"
size = 10
}
metadata = {
startup-script-url = "gs://cloud-training/gcpnet/httplb/startup.sh"
}
create_template = true
tags = [
"http-server"
]
}
module "instance_template_ue1" {
source = "../../../modules/compute-vm"
project_id = module.project.project_id
zone = "us-east1-b"
name = "${local.prefix}us-east1-template"
network_interfaces = [{
network = module.vpc.self_link
subnetwork = module.vpc.subnet_self_links["us-east1/subnet-ue1"]
nat = false
addresses = null
}]
boot_disk = {
image = "projects/debian-cloud/global/images/family/debian-11"
type = "pd-ssd"
size = 10
}
metadata = {
startup-script-url = "gs://cloud-training/gcpnet/httplb/startup.sh"
}
create_template = true
tags = [
"http-server"
]
}
module "vm_siege" {
source = "../../../modules/compute-vm"
project_id = module.project.project_id
zone = "us-west1-c"
name = "siege-vm"
instance_type = "n1-standard-2"
network_interfaces = [{
network = module.vpc.self_link
subnetwork = module.vpc.subnet_self_links["us-west1/subnet-uw1"]
nat = true
addresses = null
}]
boot_disk = {
image = "projects/debian-cloud/global/images/family/debian-11"
type = "pd-ssd"
size = 10
}
metadata = {
startup-script = <<EOT
#!/bin/bash
apt update -y
apt install -y siege
EOT
}
tags = [
"ssh"
]
}
module "mig_ew1" {
source = "../../../modules/compute-mig"
project_id = module.project.project_id
location = "europe-west1"
name = "${local.prefix}europe-west1-mig"
regional = true
default_version = {
instance_template = module.instance_template_ew1.template.self_link
name = "default"
}
autoscaler_config = {
max_replicas = 5
min_replicas = 1
cooldown_period = 45
cpu_utilization_target = 0.8
load_balancing_utilization_target = null
metric = null
}
named_ports = {
http = 80
}
depends_on = [
module.nat_ew1
]
}
module "mig_ue1" {
source = "../../../modules/compute-mig"
project_id = module.project.project_id
location = "us-east1"
name = "${local.prefix}us-east1-mig"
regional = true
default_version = {
instance_template = module.instance_template_ue1.template.self_link
name = "default"
}
autoscaler_config = {
max_replicas = 5
min_replicas = 1
cooldown_period = 45
cpu_utilization_target = 0.8
load_balancing_utilization_target = null
metric = null
}
named_ports = {
http = 80
}
depends_on = [
module.nat_ue1
]
}
module "glb" {
source = "../../../modules/net-glb"
name = "${local.prefix}http-lb"
project_id = module.project.project_id
backend_services_config = {
http-backend = {
bucket_config = null
enable_cdn = false
cdn_config = null
group_config = {
backends = [
{
group = module.mig_ew1.group_manager.instance_group
options = null
},
{
group = module.mig_ue1.group_manager.instance_group
options = null
}
],
health_checks = ["hc"]
log_config = {
enable = true
sample_rate = 1
}
options = {
affinity_cookie_ttl_sec = null
custom_request_headers = null
custom_response_headers = null
connection_draining_timeout_sec = null
load_balancing_scheme = null
locality_lb_policy = null
port_name = "http"
security_policy = try(google_compute_security_policy.policy[0].name, null)
session_affinity = null
timeout_sec = null
circuits_breakers = null
consistent_hash = null
iap = null
protocol = "HTTP"
}
}
}
}
health_checks_config = {
hc = {
type = "http"
logging = true
options = null
check = {
port_name = "http"
port_specification = "USE_NAMED_PORT"
}
}
}
}
resource "google_compute_security_policy" "policy" {
count = var.enforce_security_policy ? 1 : 0
name = "${local.prefix}denylist-siege"
project = module.project.project_id
rule {
action = "deny(403)"
priority = "1000"
match {
versioned_expr = "SRC_IPS_V1"
config {
src_ip_ranges = [module.vm_siege.external_ip]
}
}
description = "Deny access to siege VM IP"
}
rule {
action = "allow"
priority = "2147483647"
match {
versioned_expr = "SRC_IPS_V1"
config {
src_ip_ranges = ["*"]
}
}
description = "default rule"
}
}

View File

@ -0,0 +1,26 @@
/**
* 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.
*/
output "glb_ip_address" {
description = "Load balancer IP address."
value = module.glb.global_forwarding_rule.ip_address
}
output "vm_siege_external_ip" {
description = "Siege VM external IP address."
value = module.vm_siege.external_ip
}

View File

@ -0,0 +1,41 @@
/**
* 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.
*/
variable "project_create" {
description = "Parameters for the creation of the new project."
type = object({
billing_account_id = string
parent = string
})
default = null
}
variable "project_id" {
description = "Identifier of the project."
type = string
}
variable "enforce_security_policy" {
description = "Enforce security policy."
type = bool
default = true
}
variable "prefix" {
description = "Prefix used for created resources."
type = string
default = null
}

View File

@ -0,0 +1,13 @@
# 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.

View File

@ -0,0 +1,20 @@
# 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
#
# https://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.
module "test" {
source = "../../../../../examples/cloud-operations/glb_and_armor"
project_create = var.project_create
project_id = var.project_id
enforce_security_policy = var.enforce_security_policy
}

View File

@ -0,0 +1,34 @@
# 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
#
# https://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 "project_create" {
type = object({
billing_account_id = string
parent = string
})
default = {
billing_account_id = "123456789"
parent = "organizations/123456789"
}
}
variable "project_id" {
type = string
default = "project-1"
}
variable "enforce_security_policy" {
type = bool
default = true
}

View File

@ -0,0 +1,19 @@
# 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.
def test_resources(e2e_plan_runner):
"Test that plan works and the numbers of resources is as expected."
modules, resources = e2e_plan_runner()
assert len(modules) == 11
assert len(resources) == 25