-
Notifications
You must be signed in to change notification settings - Fork 922
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add filtering-proxy-psc blueprint (#962)
- Loading branch information
Showing
10 changed files
with
531 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Network filtering with Squid with isolated VPCs using Private Service Connect | ||
|
||
This blueprint shows how to deploy a filtering HTTP proxy to restrict Internet access. Here we show one way to do this using isolated VPCs and Private Service Connect: | ||
|
||
- The `app` subnet hosts the consumer VMs that will have their Internet access tightly controlled by a non-caching filtering forward proxy. | ||
- The `proxy` subnet hosts a Cloud NAT instance and a [Squid](http://www.squid-cache.org/) server. | ||
- The `psc` subnet is reserved for the Private Service Connect. | ||
|
||
The reason for using Privat Service Connect in this setup is to have a common proxy setup between all environments without having to share a VPC between projects. This allows us to enforce the `compute.vmExternalIpAccess` [organization policy](https://cloud.google.com/resource-manager/docs/organization-policy/org-policy-constraints), which prevents the service projects from having external IPs, thus forcing all outbound Internet connections through the proxy. | ||
|
||
To allow Internet connectivity to the proxy subnet, a Cloud NAT instance is configured to allow usage from [that subnet only](https://cloud.google.com/nat/docs/using-nat#specify_subnet_ranges_for_nat). All other subnets are not allowed to use the Cloud NAT instance. | ||
|
||
To simplify the usage of the proxy, a Cloud DNS private zone is created in each consumer VPC and the IP address of the proxy is exposed with the FQDN `proxy.internal`. In addition, system-wide `http_proxy` and `https_proxy` environment variables and an APT configuration are rolled out via a [startup script](startup.sh). | ||
<!-- BEGIN TFDOC --> | ||
|
||
## Variables | ||
|
||
| name | description | type | required | default | | ||
|---|---|:---:|:---:|:---:| | ||
| [prefix](variables.tf#L44) | Prefix used for resources that need unique names. | <code>string</code> | ✓ | | | ||
| [project_id](variables.tf#L66) | Project id used for all resources. | <code>string</code> | ✓ | | | ||
| [allowed_domains](variables.tf#L17) | List of domains allowed by the squid proxy. | <code>list(string)</code> | | <code title="[ ".google.com", ".github.com", ".fastlydns.net", ".debian.org" ]">[…]</code> | | ||
| [cidrs](variables.tf#L28) | CIDR ranges for subnets. | <code>map(string)</code> | | <code title="{ app = "10.0.0.0/24" proxy = "10.0.2.0/28" psc = "10.0.3.0/28" }">{…}</code> | | ||
| [nat_logging](variables.tf#L38) | Enables Cloud NAT logging if not null, value is one of 'ERRORS_ONLY', 'TRANSLATIONS_ONLY', 'ALL'. | <code>string</code> | | <code>"ERRORS_ONLY"</code> | | ||
| [project_create](variables.tf#L49) | Set to non null if project needs to be created. | <code title="object({ billing_account = string parent = string })">object({…})</code> | | <code>null</code> | | ||
| [region](variables.tf#L71) | Default region for resources. | <code>string</code> | | <code>"europe-west1"</code> | | ||
|
||
<!-- END TFDOC --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/** | ||
* 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. | ||
*/ | ||
|
||
############################################################################### | ||
# Consumer project and VPC # | ||
############################################################################### | ||
|
||
module "vpc-consumer" { | ||
source = "../../../modules/net-vpc" | ||
project_id = module.project.project_id | ||
name = "${var.prefix}-app" | ||
subnets = [ | ||
{ | ||
name = "${var.prefix}-app" | ||
ip_cidr_range = var.cidrs.app | ||
region = var.region | ||
} | ||
] | ||
} | ||
|
||
############################################################################### | ||
# Test VM # | ||
############################################################################### | ||
|
||
module "test-vm-consumer" { | ||
source = "../../../modules/compute-vm" | ||
project_id = module.project.project_id | ||
zone = "${var.region}-b" | ||
name = "${var.prefix}-test-vm" | ||
instance_type = "e2-micro" | ||
tags = ["ssh"] | ||
network_interfaces = [{ | ||
network = module.vpc-consumer.self_link | ||
subnetwork = module.vpc-consumer.subnet_self_links["${var.region}/${var.prefix}-app"] | ||
nat = false | ||
addresses = null | ||
}] | ||
boot_disk = { | ||
image = "debian-cloud/debian-10" | ||
type = "pd-standard" | ||
size = 10 | ||
} | ||
service_account_create = true | ||
metadata = { | ||
startup-script = templatefile("${path.module}/startup.sh", { proxy_url = "http://proxy.internal:3128" }) | ||
} | ||
} | ||
|
||
############################################################################### | ||
# PSC Consuner # | ||
############################################################################### | ||
|
||
resource "google_compute_address" "psc_endpoint_address" { | ||
name = "${var.prefix}-psc-proxy-address" | ||
project = module.project.project_id | ||
address_type = "INTERNAL" | ||
subnetwork = module.vpc-consumer.subnet_self_links["${var.region}/${var.prefix}-app"] | ||
region = var.region | ||
} | ||
|
||
resource "google_compute_forwarding_rule" "psc_ilb_consumer" { | ||
name = "${var.prefix}-psc-proxy-fw-rule" | ||
project = module.project.project_id | ||
region = var.region | ||
target = google_compute_service_attachment.service_attachment.id | ||
load_balancing_scheme = "" | ||
network = module.vpc-consumer.self_link | ||
ip_address = google_compute_address.psc_endpoint_address.id | ||
} | ||
|
||
############################################################################### | ||
# DNS and Firewall # | ||
############################################################################### | ||
|
||
module "private-dns" { | ||
source = "../../../modules/dns" | ||
project_id = module.project.project_id | ||
type = "private" | ||
name = "${var.prefix}-internal" | ||
domain = "internal." | ||
client_networks = [module.vpc-consumer.self_link] | ||
recordsets = { | ||
"A squid" = { ttl = 60, records = [google_compute_address.psc_endpoint_address.address] } | ||
"CNAME proxy" = { ttl = 3600, records = ["squid.internal."] } | ||
} | ||
} | ||
|
||
module "firewall-consumer" { | ||
source = "../../../modules/net-vpc-firewall" | ||
project_id = module.project.project_id | ||
network = module.vpc-consumer.name | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
/** | ||
* 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. | ||
*/ | ||
|
||
############################################################################### | ||
# Host project and VPC resources # | ||
############################################################################### | ||
|
||
module "project" { | ||
source = "../../../modules/project" | ||
project_create = var.project_create != null | ||
billing_account = try(var.project_create.billing_account, null) | ||
parent = try(var.project_create.parent, null) | ||
name = var.project_id | ||
services = [ | ||
"dns.googleapis.com", | ||
"compute.googleapis.com", | ||
"logging.googleapis.com" | ||
] | ||
} | ||
|
||
module "vpc" { | ||
source = "../../../modules/net-vpc" | ||
project_id = module.project.project_id | ||
name = "${var.prefix}-vpc" | ||
subnets = [ | ||
{ | ||
name = "proxy" | ||
ip_cidr_range = var.cidrs.proxy | ||
region = var.region | ||
} | ||
] | ||
subnets_psc = [ | ||
{ | ||
name = "psc" | ||
ip_cidr_range = var.cidrs.psc | ||
region = var.region | ||
} | ||
] | ||
} | ||
|
||
module "firewall" { | ||
source = "../../../modules/net-vpc-firewall" | ||
project_id = module.project.project_id | ||
network = module.vpc.name | ||
ingress_rules = { | ||
allow-ingress-squid = { | ||
description = "Allow squid ingress traffic" | ||
source_ranges = [ | ||
var.cidrs.psc, "35.191.0.0/16", "130.211.0.0/22" | ||
] | ||
targets = [module.service-account-squid.email] | ||
use_service_accounts = true | ||
rules = [{ | ||
protocol = "tcp" | ||
ports = [3128] | ||
}] | ||
} | ||
} | ||
} | ||
|
||
module "nat" { | ||
source = "../../../modules/net-cloudnat" | ||
project_id = module.project.project_id | ||
region = var.region | ||
name = "default" | ||
router_network = module.vpc.name | ||
config_source_subnets = "LIST_OF_SUBNETWORKS" | ||
# 64512/11 = 5864 . 11 is the number of usable IPs in the proxy subnet | ||
config_min_ports_per_vm = 5864 | ||
subnetworks = [ | ||
{ | ||
self_link = module.vpc.subnet_self_links["${var.region}/proxy"] | ||
config_source_ranges = ["ALL_IP_RANGES"] | ||
secondary_ranges = null | ||
} | ||
] | ||
logging_filter = var.nat_logging | ||
} | ||
|
||
############################################################################### | ||
# PSC resources # | ||
############################################################################### | ||
|
||
resource "google_compute_service_attachment" "service_attachment" { | ||
name = "psc" | ||
project = module.project.project_id | ||
region = var.region | ||
enable_proxy_protocol = false | ||
connection_preference = "ACCEPT_MANUAL" | ||
nat_subnets = [module.vpc.subnets_psc["${var.region}/psc"].self_link] | ||
target_service = module.squid-ilb.forwarding_rule_self_link | ||
consumer_accept_lists { | ||
project_id_or_num = module.project.project_id | ||
connection_limit = 10 | ||
} | ||
} | ||
|
||
############################################################################### | ||
# Squid resources # | ||
############################################################################### | ||
|
||
module "service-account-squid" { | ||
source = "../../../modules/iam-service-account" | ||
project_id = module.project.project_id | ||
name = "svc-squid" | ||
iam_project_roles = { | ||
(module.project.project_id) = [ | ||
"roles/logging.logWriter", | ||
"roles/monitoring.metricWriter", | ||
] | ||
} | ||
} | ||
|
||
module "cos-squid" { | ||
source = "../../../modules/cloud-config-container/squid" | ||
allow = var.allowed_domains | ||
clients = [var.cidrs.psc] | ||
} | ||
|
||
module "squid-vm" { | ||
source = "../../../modules/compute-vm" | ||
project_id = module.project.project_id | ||
zone = "${var.region}-b" | ||
name = "squid-vm" | ||
instance_type = "e2-medium" | ||
create_template = true | ||
network_interfaces = [{ | ||
network = module.vpc.self_link | ||
subnetwork = module.vpc.subnet_self_links["${var.region}/proxy"] | ||
}] | ||
boot_disk = { | ||
image = "cos-cloud/cos-stable" | ||
} | ||
service_account = module.service-account-squid.email | ||
service_account_scopes = ["https://www.googleapis.com/auth/cloud-platform"] | ||
metadata = { | ||
user-data = module.cos-squid.cloud_config | ||
} | ||
} | ||
|
||
module "squid-mig" { | ||
source = "../../../modules/compute-mig" | ||
project_id = module.project.project_id | ||
location = "${var.region}-b" | ||
name = "squid-mig" | ||
instance_template = module.squid-vm.template.self_link | ||
target_size = 1 | ||
auto_healing_policies = { | ||
initial_delay_sec = 60 | ||
} | ||
autoscaler_config = { | ||
max_replicas = 10 | ||
min_replicas = 1 | ||
cooldown_period = 30 | ||
scaling_signals = { | ||
cpu_utilization = { | ||
target = 0.65 | ||
} | ||
} | ||
} | ||
health_check_config = { | ||
enable_logging = true | ||
tcp = { | ||
port = 3128 | ||
} | ||
} | ||
update_policy = { | ||
minimal_action = "REPLACE" | ||
type = "PROACTIVE" | ||
max_surge = { | ||
fixed = 3 | ||
} | ||
min_ready_sec = 60 | ||
} | ||
} | ||
|
||
module "squid-ilb" { | ||
source = "../../../modules/net-ilb" | ||
project_id = module.project.project_id | ||
region = var.region | ||
name = "squid-ilb" | ||
ports = [3128] | ||
service_label = "squid-ilb" | ||
vpc_config = { | ||
network = module.vpc.self_link | ||
subnetwork = module.vpc.subnet_self_links["${var.region}/proxy"] | ||
} | ||
backends = [{ | ||
group = module.squid-mig.group_manager.instance_group | ||
}] | ||
health_check_config = { | ||
enable_logging = true | ||
tcp = { | ||
port = 3128 | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
|
||
cat <<EOF > /etc/apt/apt.conf.d/proxy.conf | ||
Acquire { | ||
HTTP::proxy "${proxy_url}"; | ||
HTTPS::proxy "${proxy_url}"; | ||
} | ||
EOF | ||
|
||
cat <<EOF > /etc/profile.d/proxy.sh | ||
export http_proxy="${proxy_url}" | ||
export https_proxy="${proxy_url}" | ||
export no_proxy="127.0.0.1,localhost" | ||
EOF |
Oops, something went wrong.