Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add filtering-proxy-psc blueprint #962

Merged
merged 10 commits into from
Nov 11, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions blueprints/networking/filtering-proxy-psc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# 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.

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](proxy-consumer/startup.sh).
<!-- BEGIN TFDOC -->

## Variables

| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [prefix](variables.tf#L66) | Prefix used for resources that need unique names. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L34) | Project id used for all resources. | <code>string</code> | ✓ | |
| [allowed_domains](variables.tf#L39) | 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#L50) | 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#L60) | 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#L17) | 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 -->
300 changes: 300 additions & 0 deletions blueprints/networking/filtering-proxy-psc/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
/**
* 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
kunzese marked this conversation as resolved.
Show resolved Hide resolved
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
}
}
}

###############################################################################
kunzese marked this conversation as resolved.
Show resolved Hide resolved
# 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
}
26 changes: 26 additions & 0 deletions blueprints/networking/filtering-proxy-psc/startup.sh
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
Loading