Add filtering-proxy-psc blueprint (#962)

This commit is contained in:
Sebastian Kunze 2022-11-11 11:24:37 +01:00 committed by GitHub
parent eb27635221
commit ef38d238b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 531 additions and 1 deletions

View File

@ -8,7 +8,7 @@ Currently available blueprints:
- **data solutions** - [GCE and GCS CMEK via centralized Cloud KMS](./data-solutions/cmek-via-centralized-kms), [Cloud Composer version 2 private instance, supporting Shared VPC and external CMEK key](./data-solutions/composer-2), [Cloud SQL instance with multi-region read replicas](./data-solutions/cloudsql-multiregion), [Data Platform](./data-solutions/data-platform-foundations), [Spinning up a foundation data pipeline on Google Cloud using Cloud Storage, Dataflow and BigQuery](./data-solutions/gcs-to-bq-with-least-privileges), [#SQL Server Always On Groups blueprint](./data-solutions/sqlserver-alwayson), [Data Playground](./data-solutions/data-playground)
- **factories** - [The why and the how of Resource Factories](./factories), [Google Cloud Identity Group Factory](./factories/cloud-identity-group-factory), [Google Cloud BQ Factory](./factories/bigquery-factory), [Google Cloud VPC Firewall Factory](./factories/net-vpc-firewall-yaml), [Minimal Project Factory](./factories/project-factory)
- **GKE** - [Binary Authorization Pipeline Blueprint](./gke/binauthz), [Storage API](./gke/binauthz/image), [Multi-cluster mesh on GKE (fleet API)](./gke/multi-cluster-mesh-gke-fleet-api), [GKE Multitenant Blueprint](./gke/multitenant-fleet), [Shared VPC with GKE support](./networking/shared-vpc-gke/)
- **networking** - [Decentralized firewall management](./networking/decentralized-firewall), [Decentralized firewall validator](./networking/decentralized-firewall/validator), [Network filtering with Squid](./networking/filtering-proxy), [HTTP Load Balancer with Cloud Armor](./networking/glb-and-armor), [Hub and Spoke via VPN](./networking/hub-and-spoke-vpn), [Hub and Spoke via VPC Peering](./networking/hub-and-spoke-peering), [Internal Load Balancer as Next Hop](./networking/ilb-next-hop), [Nginx-based reverse proxy cluster](./networking/nginx-reverse-proxy-cluster), [On-prem DNS and Google Private Access](./networking/onprem-google-access-dns), [Calling a private Cloud Function from On-premises](./networking/private-cloud-function-from-onprem), [Hybrid connectivity to on-premise services through PSC](./networking/psc-hybrid), [PSC Producer](./networking/psc-hybrid/psc-producer), [PSC Consumer](./networking/psc-hybrid/psc-consumer), [Shared VPC with optional GKE cluster](./networking/shared-vpc-gke)
- **networking** - [Decentralized firewall management](./networking/decentralized-firewall), [Decentralized firewall validator](./networking/decentralized-firewall/validator), [Network filtering with Squid](./networking/filtering-proxy), [Network filtering with Squid with isolated VPCs using Private Service Connect](./networking/filtering-proxy-psc), [HTTP Load Balancer with Cloud Armor](./networking/glb-and-armor), [Hub and Spoke via VPN](./networking/hub-and-spoke-vpn), [Hub and Spoke via VPC Peering](./networking/hub-and-spoke-peering), [Internal Load Balancer as Next Hop](./networking/ilb-next-hop), [Nginx-based reverse proxy cluster](./networking/nginx-reverse-proxy-cluster), [On-prem DNS and Google Private Access](./networking/onprem-google-access-dns), [Calling a private Cloud Function from On-premises](./networking/private-cloud-function-from-onprem), [Hybrid connectivity to on-premise services through PSC](./networking/psc-hybrid), [PSC Producer](./networking/psc-hybrid/psc-producer), [PSC Consumer](./networking/psc-hybrid/psc-consumer), [Shared VPC with optional GKE cluster](./networking/shared-vpc-gke)
- **serverless** - [Creating multi-region deployments for API Gateway](./serverless/api-gateway)
- **third party solutions** - [OpenShift on GCP user-provisioned infrastructure](./third-party-solutions/openshift), [Wordpress deployment on Cloud Run](./third-party-solutions/wordpress/cloudrun)

View File

@ -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&#40;string&#41;</code> | | <code title="&#91;&#10; &#34;.google.com&#34;,&#10; &#34;.github.com&#34;,&#10; &#34;.fastlydns.net&#34;,&#10; &#34;.debian.org&#34;&#10;&#93;">&#91;&#8230;&#93;</code> |
| [cidrs](variables.tf#L28) | CIDR ranges for subnets. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; app &#61; &#34;10.0.0.0&#47;24&#34;&#10; proxy &#61; &#34;10.0.2.0&#47;28&#34;&#10; psc &#61; &#34;10.0.3.0&#47;28&#34;&#10;&#125;">&#123;&#8230;&#125;</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>&#34;ERRORS_ONLY&#34;</code> |
| [project_create](variables.tf#L49) | Set to non null if project needs to be created. | <code title="object&#40;&#123;&#10; billing_account &#61; string&#10; parent &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [region](variables.tf#L71) | Default region for resources. | <code>string</code> | | <code>&#34;europe-west1&#34;</code> |
<!-- END TFDOC -->

View File

@ -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
}

View File

@ -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
}
}
}

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.
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

View File

@ -0,0 +1,75 @@
/**
* 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 "allowed_domains" {
description = "List of domains allowed by the squid proxy."
type = list(string)
default = [
".google.com",
".github.com",
".fastlydns.net",
".debian.org"
]
}
variable "cidrs" {
description = "CIDR ranges for subnets."
type = map(string)
default = {
app = "10.0.0.0/24"
proxy = "10.0.2.0/28"
psc = "10.0.3.0/28"
}
}
variable "nat_logging" {
description = "Enables Cloud NAT logging if not null, value is one of 'ERRORS_ONLY', 'TRANSLATIONS_ONLY', 'ALL'."
type = string
default = "ERRORS_ONLY"
}
variable "prefix" {
description = "Prefix used for resources that need unique names."
type = string
}
variable "project_create" {
description = "Set to non null if project needs to be created."
type = object({
billing_account = string
parent = string
})
default = null
validation {
condition = (
var.project_create == null
? true
: can(regex("(organizations|folders)/[0-9]+", var.project_create.parent))
)
error_message = "Project parent must be of the form folders/folder_id or organizations/organization_id."
}
}
variable "project_id" {
description = "Project id used for all resources."
type = string
}
variable "region" {
description = "Default region for resources."
type = string
default = "europe-west1"
}

View File

@ -0,0 +1,29 @@
# 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.
terraform {
required_version = ">= 1.3.1"
required_providers {
google = {
source = "hashicorp/google"
version = ">= 4.40.0" # tftest
}
google-beta = {
source = "hashicorp/google-beta"
version = ">= 4.40.0" # tftest
}
}
}

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,25 @@
/**
* 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.
*/
module "test" {
source = "../../../../../blueprints/networking/filtering-proxy-psc"
prefix = "fabric"
project_create = {
billing_account = "123456-ABCDEF-123456"
parent = "folders/1234567890"
}
project_id = "test-project"
}

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) == 12
assert len(resources) == 33