diff --git a/modules/cloud-config-container/squid/README.md b/modules/cloud-config-container/squid/README.md new file mode 100644 index 0000000000..be982fb998 --- /dev/null +++ b/modules/cloud-config-container/squid/README.md @@ -0,0 +1,80 @@ +# Containerized Squid on Container Optimized OS + +This module manages a `cloud-config` configuration that starts a containerized [Squid](http://www.squid-cache.org/) proxy on Container Optimized OS. The default configuration creates a filtering proxy that only allows connection to a whitelisted set of domains. + +The resulting `cloud-config` can be customized in a number of ways: + +- a custom squid.conf configuration can be set using the `squid_config` variable +- additional files (e.g. additional acls) can be passed in via the `files` variable +- a completely custom `cloud-config` can be passed in via the `cloud_config` variable, and additional template variables can be passed in via `config_variables` + +The default instance configuration inserts iptables rules to allow traffic on TCP port 3128. + +Logging and monitoring are enabled via the [Google Cloud Logging driver](https://docs.docker.com/config/containers/logging/gcplogs/) configured for the Squid container, and the [Node Problem Detector](https://cloud.google.com/container-optimized-os/docs/how-to/monitoring) service started by default on boot. + +The module renders the generated cloud config in the `cloud_config` output, to be used in instances or instance templates via the `user-data` metadata. + +For convenience during development or for simple use cases, the module can optionally manage a single instance via the `test_instance` variable. If the instance is not needed the `instance*tf` files can be safely removed. Refer to the [top-level README](../README.md) for more details on the included instance. + +## Examples + +### Default Squid configuration + +This example will create a `cloud-config` that allows any client in the 10.0.0.0/8 CIDR to use the proxy to connect github.com or any subdomain of github.com. + +```hcl +module "cos-squid" { + source = "./modules/cos-container/squid" + whitelist = [".github.com"] + clients = ["10.0.0.0/8"] +} + +# use it as metadata in a compute instance or template +resource "google_compute_instance" "default" { + metadata = { + user-data = module.cos-squid.cloud_config + } +``` + +### Test Squid instance + +This example shows how to create the single instance optionally managed by the module, providing all required attributes in the `test_instance` variable. The instance is purposefully kept simple and should only be used in development, or when designing infrastructures. + +```hcl +module "cos-squid" { + source = "./modules/cos-container/squid" + whitelist = ["github.com"] + clients = ["10.0.0.0/8"] + test_instance = { + project_id = "my-project" + zone = "europe-west1-b" + name = "cos-squid" + type = "f1-micro" + network = "default" + subnetwork = "https://www.googleapis.com/compute/v1/projects/my-project/regions/europe-west1/subnetworks/my-subnet" + } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| *clients* | List of CIDRs from which Squid will allow connections | list(string) | | [] | +| *cloud_config* | Cloud config template path. If null default will be used. | string | | null | +| *config_variables* | Additional variables used to render the cloud-config and Squid templates. | map(any) | | {} | +| *file_defaults* | Default owner and permissions for files. | object({...}) | | ... | +| *files* | Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null. | map(object({...})) | | {} | +| *squid_config* | Squid configuration path, if null default will be used. | string | | null | +| *test_instance* | Test/development instance attributes, leave null to skip creation. | object({...}) | | null | +| *test_instance_defaults* | Test/development instance defaults used for optional configuration. If image is null, COS stable will be used. | object({...}) | | ... | +| *whitelist* | List of domains Squid will allow connections to | list(string) | | [] | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| cloud_config | Rendered cloud-config file to be passed as user-data instance metadata. | | +| test_instance | Optional test instance name and address | | + diff --git a/modules/cloud-config-container/squid/cloud-config.yaml b/modules/cloud-config-container/squid/cloud-config.yaml new file mode 100644 index 0000000000..68dd2fb87f --- /dev/null +++ b/modules/cloud-config-container/squid/cloud-config.yaml @@ -0,0 +1,88 @@ +#cloud-config + +# Copyright 2020 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. + +# TODO: switch to the gcplogs logging driver, and set driver labels + +users: +- name: squid + uid: 2000 + +write_files: + - path: /var/lib/docker/daemon.json + permissions: 0644 + owner: root + content: | + { + "live-restore": true, + "storage-driver": "overlay2", + "log-opts": { + "max-size": "1024m" + } + } + + - path: /etc/squid/squid.conf + permissions: 0644 + owner: root + content: | + ${indent(6, squid_config)} + + - path: /etc/squid/whitelist.txt + permissions: 0644 + owner: root + content: | + ${indent(6, join("\n", whitelist))} + + - path: /etc/squid/clients.txt + permissions: 0644 + owner: root + content: | + ${indent(6, join("\n", clients))} + + # squid container service + - path: /etc/systemd/system/squid.service + permissions: 0644 + owner: root + content: | + [Unit] + Description=Start squid container + After=gcr-online.target docker.socket + Wants=gcr-online.target docker.socket docker-events-collector.service + + [Service] + Environment="HOME=/home/squid" + ExecStartPre=/usr/bin/docker-credential-gcr configure-docker + ExecStart=/usr/bin/docker run --rm --name=squid \ + --log-driver=gcplogs --network host \ + -v /etc/squid:/etc/squid \ + gcr.io/pso-cft-fabric/squid:0.10 + ExecStop=/usr/bin/docker stop squid + ExecStopPost=/usr/bin/docker rm squid + + %{ for path, data in files } + - path: ${path} + owner: ${lookup(data, "owner", "root")} + permissions: ${lookup(data, "permissions", "0644")} + content: | + ${indent(4, data.content)} + %{ endfor } + +bootcmd: + - systemctl start node-problem-detector + +runcmd: + - iptables -I INPUT 1 -p tcp -m tcp --dport 3128 -m state --state NEW,ESTABLISHED -j ACCEPT + - systemctl daemon-reload + - systemctl start squid diff --git a/modules/cloud-config-container/squid/instance.tf b/modules/cloud-config-container/squid/instance.tf new file mode 120000 index 0000000000..bdef596b6d --- /dev/null +++ b/modules/cloud-config-container/squid/instance.tf @@ -0,0 +1 @@ +../instance.tf \ No newline at end of file diff --git a/modules/cloud-config-container/squid/main.tf b/modules/cloud-config-container/squid/main.tf new file mode 100644 index 0000000000..d2c7aca0cb --- /dev/null +++ b/modules/cloud-config-container/squid/main.tf @@ -0,0 +1,45 @@ +/** + * Copyright 2019 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 { + cloud_config = templatefile(local.template, merge(local.config_variables, { + squid_config = templatefile(local.squid_config, local.config_variables) + files = local.files + })) + squid_config = ( + var.squid_config == null ? "${path.module}/squid.conf" : var.squid_config + ) + files = { + for path, attrs in var.files : path => { + content = attrs.content, + owner = attrs.owner == null ? var.file_defaults.owner : attrs.owner, + permissions = ( + attrs.permissions == null + ? var.file_defaults.permissions + : attrs.permissions + ) + } + } + template = ( + var.cloud_config == null + ? "${path.module}/cloud-config.yaml" + : var.cloud_config + ) + config_variables = merge(var.config_variables, { + whitelist = var.whitelist + clients = var.clients + }) +} diff --git a/modules/cloud-config-container/squid/outputs-instance.tf b/modules/cloud-config-container/squid/outputs-instance.tf new file mode 120000 index 0000000000..ea9e240458 --- /dev/null +++ b/modules/cloud-config-container/squid/outputs-instance.tf @@ -0,0 +1 @@ +../outputs-instance.tf \ No newline at end of file diff --git a/modules/cloud-config-container/squid/outputs.tf b/modules/cloud-config-container/squid/outputs.tf new file mode 100644 index 0000000000..205a557165 --- /dev/null +++ b/modules/cloud-config-container/squid/outputs.tf @@ -0,0 +1,20 @@ +/** + * Copyright 2019 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 "cloud_config" { + description = "Rendered cloud-config file to be passed as user-data instance metadata." + value = local.cloud_config +} diff --git a/modules/cloud-config-container/squid/squid.conf b/modules/cloud-config-container/squid/squid.conf new file mode 100644 index 0000000000..2507c22044 --- /dev/null +++ b/modules/cloud-config-container/squid/squid.conf @@ -0,0 +1,34 @@ +# bind to port 3128 +http_port 0.0.0.0:3128 + +# only proxy, don't cache +cache deny all + +acl ssl_ports port 443 +acl safe_ports port 80 +acl safe_ports port 443 +acl CONNECT method CONNECT + +# read clientd cidr from clients.txt +acl clients src "/etc/squid/clients.txt" + +# read whitelisted domains from whitelist.txt +acl whitelist dstdomain "/etc/squid/whitelist.txt" + +# deny access to anything other than ports 80 and 443 +http_access deny !safe_ports + +# deny CONNECT if connection is not using ssl +http_access deny CONNECT !ssl_ports + +# deny acccess to cachemgr +http_access deny manager + +# deny access to localhost though the proxy +http_access deny to_localhost + +# allow connection from allowed clients only to the whitelisted domains +http_access allow clients whitelist + +# deny everything else +http_access deny all diff --git a/modules/cloud-config-container/squid/variables-instance.tf b/modules/cloud-config-container/squid/variables-instance.tf new file mode 120000 index 0000000000..94af61e4dd --- /dev/null +++ b/modules/cloud-config-container/squid/variables-instance.tf @@ -0,0 +1 @@ +../variables-instance.tf \ No newline at end of file diff --git a/modules/cloud-config-container/squid/variables.tf b/modules/cloud-config-container/squid/variables.tf new file mode 100644 index 0000000000..cb88040166 --- /dev/null +++ b/modules/cloud-config-container/squid/variables.tf @@ -0,0 +1,67 @@ +/** + * Copyright 2019 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 "cloud_config" { + description = "Cloud config template path. If null default will be used." + type = string + default = null +} + +variable "config_variables" { + description = "Additional variables used to render the cloud-config and Squid templates." + type = map(any) + default = {} +} + +variable "squid_config" { + description = "Squid configuration path, if null default will be used." + type = string + default = null +} + +variable "file_defaults" { + description = "Default owner and permissions for files." + type = object({ + owner = string + permissions = string + }) + default = { + owner = "root" + permissions = "0644" + } +} + +variable "files" { + description = "Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null." + type = map(object({ + content = string + owner = string + permissions = string + })) + default = {} +} + +variable "whitelist" { + description = "List of domains Squid will allow connections to" + type = list(string) + default = [] +} + +variable "clients" { + description = "List of CIDRs from which Squid will allow connections" + type = list(string) + default = [] +}