Refactor the onprem module (#55)
* move onprem to cos-container * compute-vm: fix external addresses output * folders-unit: update README * update onprem module, add new fields to cos-container test instance * coredns: process corefile as a template * onprem: fixes * modules/cos-container: rename to cloud-config-container infra/onprem: remove test output * Update README.md * update CHANGELOG for v1.1.0 * fix cloud config modules tests * Update main.tf * add container nginx module
This commit is contained in:
parent
c486bfc66f
commit
409407ae7d
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -4,10 +4,18 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.1.0] - 2020-03-27
|
||||||
|
|
||||||
|
- rename the `cos-container` suite of modules to `cloud-config-container`
|
||||||
|
- refactor the `onprem-in-a-box` module to only manage the `cloud-config` configuration, and make it part of the `cloud-config-container` suite of modules
|
||||||
|
- update the `onprem-google-access-dns` example to use the refactored `onprem` module
|
||||||
|
- fix the `external_addresses` output in the `compute-vm` module
|
||||||
|
- small tweaks and fixes to the `cloud-config-container` modules
|
||||||
|
|
||||||
## [1.0.0] - 2020-03-27
|
## [1.0.0] - 2020-03-27
|
||||||
|
|
||||||
- merge development branch with suite of new modules and end-to-end examples
|
- merge development branch with suite of new modules and end-to-end examples
|
||||||
|
|
||||||
|
|
||||||
[Unreleased]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v1.0.0...HEAD
|
[Unreleased]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v1.0.0...HEAD
|
||||||
|
[1.1.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v1.0.0...v1.1.0
|
||||||
[1.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v0.1...v1.0
|
[1.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v0.1...v1.0
|
||||||
|
|
|
@ -34,8 +34,8 @@ Currently available modules:
|
||||||
|
|
||||||
- **foundational** - [folders](./modules/folders), [log sinks](./modules/logging-sinks), [project](./modules/project), [service accounts](./modules/iam-service-accounts)
|
- **foundational** - [folders](./modules/folders), [log sinks](./modules/logging-sinks), [project](./modules/project), [service accounts](./modules/iam-service-accounts)
|
||||||
- **networking** - [VPC](./modules/net-vpc), [VPC firewall](./modules/net-vpc-firewall), [VPC peering](./modules/net-vpc-peering), VPN ([static](./modules/net-vpn-static), [dynamic](./modules/net-vpn-dynamic), [HA](./modules/net-vpn-ha)), [NAT](./modules/net-cloudnat), [address reservation](./modules/net-address), [DNS](./modules/dns)
|
- **networking** - [VPC](./modules/net-vpc), [VPC firewall](./modules/net-vpc-firewall), [VPC peering](./modules/net-vpc-peering), VPN ([static](./modules/net-vpn-static), [dynamic](./modules/net-vpn-dynamic), [HA](./modules/net-vpn-ha)), [NAT](./modules/net-cloudnat), [address reservation](./modules/net-address), [DNS](./modules/dns)
|
||||||
- **compute** - [VM/VM group](./modules/compute-vm), [GKE cluster](./modules/gke-cluster), [GKE nodepool](./modules/gke-nodepool), [COS container](./modules/compute-vm-cos-coredns)
|
- **compute** - [VM/VM group](./modules/compute-vm), [GKE cluster](./modules/gke-cluster), [GKE nodepool](./modules/gke-nodepool), [COS container](./modules/cos-container)
|
||||||
- **data** - [GCS](./modules/gcs), [BigQuery dataset](./modules/bigquery)
|
- **data** - [GCS](./modules/gcs), [BigQuery dataset](./modules/bigquery)
|
||||||
- **other** - [KMS](./modules/kms), [on-premises in Docker](./modules/on-prem-in-a-box)
|
- **security** - [KMS](./modules/kms)
|
||||||
|
|
||||||
For more information and usage examples see each module's README file.
|
For more information and usage examples see each module's README file.
|
||||||
|
|
|
@ -96,12 +96,13 @@ sudo docker exec -it onprem_bird_1 ip route |grep bird
|
||||||
10.0.0.0/24 via 169.254.1.1 dev vti0 proto bird src 10.0.16.2
|
10.0.0.0/24 via 169.254.1.1 dev vti0 proto bird src 10.0.16.2
|
||||||
35.199.192.0/19 via 169.254.1.1 dev vti0 proto bird src 10.0.16.2
|
35.199.192.0/19 via 169.254.1.1 dev vti0 proto bird src 10.0.16.2
|
||||||
199.36.153.4/30 via 169.254.1.1 dev vti0 proto bird src 10.0.16.2
|
199.36.153.4/30 via 169.254.1.1 dev vti0 proto bird src 10.0.16.2
|
||||||
|
199.36.153.8/30 via 169.254.1.1 dev vti0 proto bird src 10.0.16.2
|
||||||
|
|
||||||
# get a shell on the toolbox container
|
# get a shell on the toolbox container
|
||||||
sudo docker exec -it onprem_toolbox_1 sh
|
sudo docker exec -it onprem_toolbox_1 sh
|
||||||
|
|
||||||
# test forwarding from CoreDNS via the Cloud DNS inbound policy
|
# test forwarding from CoreDNS via the Cloud DNS inbound policy
|
||||||
dig test-1.gcp.example.com +short
|
dig test-1.gcp.example.org +short
|
||||||
10.0.0.3
|
10.0.0.3
|
||||||
|
|
||||||
# test that Private Access is configured correctly
|
# test that Private Access is configured correctly
|
||||||
|
@ -124,8 +125,8 @@ gcloud compute instances list
|
||||||
gcloud compute ssh test-1
|
gcloud compute ssh test-1
|
||||||
|
|
||||||
# test forwarding from Cloud DNS to onprem CoreDNS (address may differ)
|
# test forwarding from Cloud DNS to onprem CoreDNS (address may differ)
|
||||||
dig gw.onprem.example.com +short
|
dig gw.onprem.example.org +short
|
||||||
10.0.16.2
|
10.0.16.1
|
||||||
|
|
||||||
# test a request to the onprem web server
|
# test a request to the onprem web server
|
||||||
curl www.onprem.example.com -s |grep h1
|
curl www.onprem.example.com -s |grep h1
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
onprem.example.com {
|
onprem.example.org {
|
||||||
root /etc/coredns
|
root /etc/coredns
|
||||||
hosts onprem.hosts
|
hosts onprem.hosts
|
||||||
log
|
log
|
||||||
errors
|
errors
|
||||||
}
|
}
|
||||||
gcp.example.com googleapis.com {
|
gcp.example.org googleapis.com {
|
||||||
forward . ${forwarder_address}
|
forward . ${dns_forwarder_address}
|
||||||
log
|
log
|
||||||
errors
|
errors
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
#
|
||||||
|
# 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 {
|
||||||
|
backend "gcs" {
|
||||||
|
bucket = ""
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,11 +18,13 @@ locals {
|
||||||
bgp_interface_gcp = "${cidrhost(var.bgp_interface_ranges.gcp, 1)}"
|
bgp_interface_gcp = "${cidrhost(var.bgp_interface_ranges.gcp, 1)}"
|
||||||
bgp_interface_onprem = "${cidrhost(var.bgp_interface_ranges.gcp, 2)}"
|
bgp_interface_onprem = "${cidrhost(var.bgp_interface_ranges.gcp, 2)}"
|
||||||
netblocks = {
|
netblocks = {
|
||||||
dns = data.google_netblock_ip_ranges.dns-forwarders.cidr_blocks_ipv4.0
|
dns = data.google_netblock_ip_ranges.dns-forwarders.cidr_blocks_ipv4.0
|
||||||
api = data.google_netblock_ip_ranges.private-googleapis.cidr_blocks_ipv4.0
|
private = data.google_netblock_ip_ranges.private-googleapis.cidr_blocks_ipv4.0
|
||||||
|
restricted = data.google_netblock_ip_ranges.restricted-googleapis.cidr_blocks_ipv4.0
|
||||||
}
|
}
|
||||||
vips = {
|
vips = {
|
||||||
api = [for i in range(4) : cidrhost(local.netblocks.api, i)]
|
private = [for i in range(4) : cidrhost(local.netblocks.private, i)]
|
||||||
|
restricted = [for i in range(4) : cidrhost(local.netblocks.restricted, i)]
|
||||||
}
|
}
|
||||||
vm-startup-script = join("\n", [
|
vm-startup-script = join("\n", [
|
||||||
"#! /bin/bash",
|
"#! /bin/bash",
|
||||||
|
@ -30,12 +32,16 @@ locals {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data "google_netblock_ip_ranges" "dns-forwarders" {
|
||||||
|
range_type = "dns-forwarders"
|
||||||
|
}
|
||||||
|
|
||||||
data "google_netblock_ip_ranges" "private-googleapis" {
|
data "google_netblock_ip_ranges" "private-googleapis" {
|
||||||
range_type = "private-googleapis"
|
range_type = "private-googleapis"
|
||||||
}
|
}
|
||||||
|
|
||||||
data "google_netblock_ip_ranges" "dns-forwarders" {
|
data "google_netblock_ip_ranges" "restricted-googleapis" {
|
||||||
range_type = "dns-forwarders"
|
range_type = "restricted-googleapis"
|
||||||
}
|
}
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
@ -80,15 +86,16 @@ module "vpn" {
|
||||||
bgp_peer_options = {
|
bgp_peer_options = {
|
||||||
advertise_groups = ["ALL_SUBNETS"]
|
advertise_groups = ["ALL_SUBNETS"]
|
||||||
advertise_ip_ranges = {
|
advertise_ip_ranges = {
|
||||||
(local.netblocks.api) = "private-googleapis"
|
(local.netblocks.dns) = "DNS resolvers"
|
||||||
(local.netblocks.dns) = "dns-forwarders"
|
(local.netblocks.private) = "private.gooogleapis.com"
|
||||||
|
(local.netblocks.restricted) = "restricted.gooogleapis.com"
|
||||||
}
|
}
|
||||||
advertise_mode = "CUSTOM"
|
advertise_mode = "CUSTOM"
|
||||||
route_priority = 1000
|
route_priority = 1000
|
||||||
}
|
}
|
||||||
bgp_session_range = "${local.bgp_interface_gcp}/30"
|
bgp_session_range = "${local.bgp_interface_gcp}/30"
|
||||||
ike_version = 2
|
ike_version = 2
|
||||||
peer_ip = module.on-prem.external_address
|
peer_ip = module.vm-onprem.external_ips.0
|
||||||
shared_secret = ""
|
shared_secret = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,7 +119,7 @@ module "dns-gcp" {
|
||||||
project_id = var.project_id
|
project_id = var.project_id
|
||||||
type = "private"
|
type = "private"
|
||||||
name = "gcp-example"
|
name = "gcp-example"
|
||||||
domain = "gcp.example.com."
|
domain = "gcp.example.org."
|
||||||
client_networks = [module.vpc.self_link]
|
client_networks = [module.vpc.self_link]
|
||||||
recordsets = concat(
|
recordsets = concat(
|
||||||
[{ name = "localhost", type = "A", ttl = 300, records = ["127.0.0.1"] }],
|
[{ name = "localhost", type = "A", ttl = 300, records = ["127.0.0.1"] }],
|
||||||
|
@ -131,12 +138,9 @@ module "dns-api" {
|
||||||
domain = "googleapis.com."
|
domain = "googleapis.com."
|
||||||
client_networks = [module.vpc.self_link]
|
client_networks = [module.vpc.self_link]
|
||||||
recordsets = [
|
recordsets = [
|
||||||
{
|
{ name = "*", type = "CNAME", ttl = 300, records = ["private.googleapis.com."] },
|
||||||
name = "*", type = "CNAME", ttl = 300, records = ["private.googleapis.com."]
|
{ name = "private", type = "A", ttl = 300, records = local.vips.private },
|
||||||
},
|
{ name = "restricted", type = "A", ttl = 300, records = local.vips.restricted },
|
||||||
{
|
|
||||||
name = "private", type = "A", ttl = 300, records = local.vips.api
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +149,7 @@ module "dns-onprem" {
|
||||||
project_id = var.project_id
|
project_id = var.project_id
|
||||||
type = "forwarding"
|
type = "forwarding"
|
||||||
name = "onprem-example"
|
name = "onprem-example"
|
||||||
domain = "onprem.example.com."
|
domain = "onprem.example.org."
|
||||||
client_networks = [module.vpc.self_link]
|
client_networks = [module.vpc.self_link]
|
||||||
forwarders = [cidrhost(var.ip_ranges.onprem, 3)]
|
forwarders = [cidrhost(var.ip_ranges.onprem, 3)]
|
||||||
}
|
}
|
||||||
|
@ -198,10 +202,21 @@ module "vm-test" {
|
||||||
# On prem #
|
# On prem #
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
data "template_file" "corefile" {
|
module "config-onprem" {
|
||||||
template = file("assets/Corefile")
|
source = "../../modules/cloud-config-container/onprem"
|
||||||
vars = {
|
config_variables = { dns_forwarder_address = var.dns_forwarder_address }
|
||||||
forwarder_address = var.forwarder_address
|
coredns_config = "assets/Corefile"
|
||||||
|
local_ip_cidr_range = var.ip_ranges.onprem
|
||||||
|
vpn_config = {
|
||||||
|
peer_ip = module.vpn.address
|
||||||
|
shared_secret = module.vpn.random_secret
|
||||||
|
type = "dynamic"
|
||||||
|
}
|
||||||
|
vpn_dynamic_config = {
|
||||||
|
local_bgp_asn = var.bgp_asn.onprem
|
||||||
|
local_bgp_address = local.bgp_interface_onprem
|
||||||
|
peer_bgp_asn = var.bgp_asn.gcp
|
||||||
|
peer_bgp_address = local.bgp_interface_gcp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,27 +233,28 @@ module "service-account-onprem" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module "on-prem" {
|
module "vm-onprem" {
|
||||||
source = "../../modules/on-prem-in-a-box/"
|
source = "../../modules/compute-vm"
|
||||||
project_id = var.project_id
|
project_id = var.project_id
|
||||||
zone = "${var.region}-b"
|
region = var.region
|
||||||
network = module.vpc.name
|
zone = "${var.region}-b"
|
||||||
subnet_self_link = module.vpc.subnet_self_links.default
|
instance_type = "f1-micro"
|
||||||
local_ip_cidr_range = var.ip_ranges.onprem
|
name = "onprem"
|
||||||
coredns_config = data.template_file.corefile.rendered
|
boot_disk = {
|
||||||
vpn_config = {
|
image = "ubuntu-os-cloud/ubuntu-1804-lts"
|
||||||
peer_ip = module.vpn.address
|
type = "pd-ssd"
|
||||||
shared_secret = module.vpn.random_secret
|
size = 10
|
||||||
type = "dynamic"
|
|
||||||
}
|
}
|
||||||
vpn_dynamic_config = {
|
metadata = {
|
||||||
local_bgp_asn = var.bgp_asn.onprem
|
user-data = module.config-onprem.cloud_config
|
||||||
local_bgp_address = local.bgp_interface_onprem
|
|
||||||
peer_bgp_asn = var.bgp_asn.gcp
|
|
||||||
peer_bgp_address = local.bgp_interface_gcp
|
|
||||||
}
|
|
||||||
service_account = {
|
|
||||||
email = module.service-account-onprem.email
|
|
||||||
scopes = ["https://www.googleapis.com/auth/cloud-platform"]
|
|
||||||
}
|
}
|
||||||
|
network_interfaces = [{
|
||||||
|
network = module.vpc.name
|
||||||
|
subnetwork = module.vpc.subnet_self_links.default
|
||||||
|
nat = true,
|
||||||
|
addresses = null
|
||||||
|
}]
|
||||||
|
service_account = module.service-account-onprem.email
|
||||||
|
service_account_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
|
||||||
|
tags = ["ssh"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,11 @@
|
||||||
|
|
||||||
output "onprem-instance" {
|
output "onprem-instance" {
|
||||||
description = "Onprem instance details."
|
description = "Onprem instance details."
|
||||||
value = join(" ", [
|
value = {
|
||||||
module.on-prem.instance_name,
|
external_ip = module.vm-onprem.external_ips.0
|
||||||
module.on-prem.internal_address,
|
internal_ip = module.vm-onprem.internal_ips.0
|
||||||
module.on-prem.external_address
|
name = module.vm-onprem.names.0
|
||||||
])
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output "test-instance" {
|
output "test-instance" {
|
||||||
|
@ -30,10 +30,3 @@ output "test-instance" {
|
||||||
module.vm-test.internal_ips[0]
|
module.vm-test.internal_ips[0]
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
output "foo" {
|
|
||||||
value = {
|
|
||||||
dns = data.google_netblock_ip_ranges.dns-forwarders.cidr_blocks_ipv4
|
|
||||||
apis = data.google_netblock_ip_ranges.private-googleapis.cidr_blocks_ipv4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -31,6 +31,12 @@ variable "bgp_interface_ranges" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "dns_forwarder_address" {
|
||||||
|
description = "Address of the DNS server used to forward queries from on-premises."
|
||||||
|
type = string
|
||||||
|
default = "10.0.0.2"
|
||||||
|
}
|
||||||
|
|
||||||
variable "ip_ranges" {
|
variable "ip_ranges" {
|
||||||
description = "IP CIDR ranges."
|
description = "IP CIDR ranges."
|
||||||
type = map(string)
|
type = map(string)
|
||||||
|
|
|
@ -31,7 +31,7 @@ Specific modules also offer support for non-authoritative bindings (e.g. `google
|
||||||
|
|
||||||
## Compute/Container
|
## Compute/Container
|
||||||
|
|
||||||
- [COS container](./modules/compute-vm-cos-coredns)
|
- [COS container](./modules/cos-container) (coredns, mysql, onprem)
|
||||||
- [GKE cluster](./modules/gke-cluster)
|
- [GKE cluster](./modules/gke-cluster)
|
||||||
- [GKE nodepool](./modules/gke-nodepool)
|
- [GKE nodepool](./modules/gke-nodepool)
|
||||||
- [VM/VM group](./modules/compute-vm)
|
- [VM/VM group](./modules/compute-vm)
|
||||||
|
@ -41,7 +41,6 @@ Specific modules also offer support for non-authoritative bindings (e.g. `google
|
||||||
- [BigQuery dataset](./modules/bigquery)
|
- [BigQuery dataset](./modules/bigquery)
|
||||||
- [GCS](./modules/gcs)
|
- [GCS](./modules/gcs)
|
||||||
|
|
||||||
## Other
|
## Security
|
||||||
|
|
||||||
- [Cloud KMS](./modules/kms)
|
- [Cloud KMS](./modules/kms)
|
||||||
- [on-premises in Docker](./modules/on-prem-in-a-box)
|
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
# Container Optimized OS modules
|
# Instance Configuration via `cloud-config`
|
||||||
|
|
||||||
This set of modules creates specialized [cloud-config](https://cloud.google.com/container-optimized-os/docs/how-to/run-container-instance#starting_a_docker_container_via_cloud-config) configurations for [Container Optimized OS](https://cloud.google.com/container-optimized-os/docs), that are used to quickly spin up containerized services for DNS, HTTP, or databases.
|
This set of modules creates specialized [cloud-config](https://cloud.google.com/container-optimized-os/docs/how-to/run-container-instance#starting_a_docker_container_via_cloud-config) configurations, which are designed for use with [Container Optimized OS](https://cloud.google.com/container-optimized-os/docs) (the [onprem module](./onprem/) is the only exception) but can also be used as a basis for other image types or cloud providers.
|
||||||
|
|
||||||
It's meant to fullfill different use cases:
|
These modules are designed for several use cases:
|
||||||
|
|
||||||
- when designing, to quickly prototype specialized services (eg MySQL access or HTTP serving)
|
- to quickly prototype specialized services (eg MySQL access or HTTP serving) for prototyping infrastructure
|
||||||
- when planning migrations, to emulate production services for core infrastructure or perfomance testing
|
- to emulate production services for perfomance testing
|
||||||
- in production, to easily add glue components for services like DNS (eg to work around inbound/outbound forwarding limitations)
|
- to easily add glue components for services like DNS (eg to work around inbound/outbound forwarding limitations)
|
||||||
- as a basis to implement cloud-native production deployments that leverage cloud-init for configuration management
|
- to implement cloud-native production deployments that leverage cloud-init for configuration management, without the need of a separate tool
|
||||||
|
|
||||||
## Available modules
|
## Available modules
|
||||||
|
|
||||||
- [CoreDNS](./coredns)
|
- [CoreDNS](./coredns)
|
||||||
- [MySQL](./mysql)
|
- [MySQL](./mysql)
|
||||||
- [ ] Nginx
|
- [Nginx](./nginx)
|
||||||
|
- [On-prem in Docker](./onprem)
|
||||||
- [ ] Squid forward proxy
|
- [ ] Squid forward proxy
|
||||||
|
|
||||||
## Using the modules
|
## Using the modules
|
||||||
|
@ -23,3 +24,7 @@ All modules are designed to be as lightweight as possible, so that specialized m
|
||||||
To use the modules with instances or instance templates, simply set use their `cloud_config` output for the `user-data` metadata. When updating the metadata after a variable change remember to manually restart the instances that use a module's output, or the changes won't effect the running system.
|
To use the modules with instances or instance templates, simply set use their `cloud_config` output for the `user-data` metadata. When updating the metadata after a variable change remember to manually restart the instances that use a module's output, or the changes won't effect the running system.
|
||||||
|
|
||||||
For convenience when developing or prototyping infrastructure, an optional test instance is included in all modules. If it's not needed, the linked `*instance.tf` files can be removed from the modules without harm.
|
For convenience when developing or prototyping infrastructure, an optional test instance is included in all modules. If it's not needed, the linked `*instance.tf` files can be removed from the modules without harm.
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- [ ] convert all `xxx_config` variables to use file content instead of path
|
|
@ -63,11 +63,8 @@ module "cos-coredns" {
|
||||||
zone = "europe-west1-b"
|
zone = "europe-west1-b"
|
||||||
name = "cos-coredns"
|
name = "cos-coredns"
|
||||||
type = "f1-micro"
|
type = "f1-micro"
|
||||||
tags = ["ssh"]
|
|
||||||
metadata = {}
|
|
||||||
network = "default"
|
network = "default"
|
||||||
subnetwork = "https://www.googleapis.com/compute/v1/projects/my-project/regions/europe-west1/subnetworks/my-subnet"
|
subnetwork = "https://www.googleapis.com/compute/v1/projects/my-project/regions/europe-west1/subnetworks/my-subnet"
|
||||||
disks = []
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -82,7 +79,8 @@ module "cos-coredns" {
|
||||||
| *coredns_config* | CoreDNS configuration path, if null default will be used. | <code title="">string</code> | | <code title="">null</code> |
|
| *coredns_config* | CoreDNS configuration path, if null default will be used. | <code title="">string</code> | | <code title="">null</code> |
|
||||||
| *file_defaults* | Default owner and permissions for files. | <code title="object({ owner = string permissions = string })">object({...})</code> | | <code title="{ owner = "root" permissions = "0644" }">...</code> |
|
| *file_defaults* | Default owner and permissions for files. | <code title="object({ owner = string permissions = string })">object({...})</code> | | <code title="{ owner = "root" permissions = "0644" }">...</code> |
|
||||||
| *files* | Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null. | <code title="map(object({ content = string owner = string permissions = string }))">map(object({...}))</code> | | <code title="">{}</code> |
|
| *files* | Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null. | <code title="map(object({ content = string owner = string permissions = string }))">map(object({...}))</code> | | <code title="">{}</code> |
|
||||||
| *test_instance* | Test/development instance attributes, leave null to skip creation. | <code title="object({ project_id = string zone = string name = string type = string tags = list(string) metadata = map(string) network = string subnetwork = string disks = map(object({ read_only = bool size = number })) })">object({...})</code> | | <code title="">null</code> |
|
| *test_instance* | Test/development instance attributes, leave null to skip creation. | <code title="object({ project_id = string zone = string name = string type = string network = string subnetwork = string })">object({...})</code> | | <code title="">null</code> |
|
||||||
|
| *test_instance_defaults* | Test/development instance defaults used for optional configuration. | <code title="object({ disks = map(object({ read_only = bool size = number })) metadata = map(string) service_account_roles = list(string) tags = list(string) })">object({...})</code> | | <code title="{ disks = {} metadata = {} service_account_roles = [ "roles/logging.logWriter", "roles/monitoring.metricWriter" ] tags = ["ssh"] }">...</code> |
|
||||||
|
|
||||||
## Outputs
|
## Outputs
|
||||||
|
|
|
@ -16,10 +16,10 @@
|
||||||
|
|
||||||
locals {
|
locals {
|
||||||
cloud_config = templatefile(local.template, merge(var.config_variables, {
|
cloud_config = templatefile(local.template, merge(var.config_variables, {
|
||||||
corefile = local.corefile
|
corefile = templatefile(local.corefile, var.config_variables)
|
||||||
files = local.files
|
files = local.files
|
||||||
}))
|
}))
|
||||||
corefile = file(
|
corefile = (
|
||||||
var.coredns_config == null ? "${path.module}/Corefile" : var.coredns_config
|
var.coredns_config == null ? "${path.module}/Corefile" : var.coredns_config
|
||||||
)
|
)
|
||||||
files = {
|
files = {
|
|
@ -21,7 +21,7 @@ variable "cloud_config" {
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "config_variables" {
|
variable "config_variables" {
|
||||||
description = "Additional variables used to render the cloud-config template."
|
description = "Additional variables used to render the cloud-config and CoreDNS templates."
|
||||||
type = map(any)
|
type = map(any)
|
||||||
default = {}
|
default = {}
|
||||||
}
|
}
|
|
@ -14,32 +14,35 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
locals {
|
|
||||||
disks = var.test_instance == null ? {} : var.test_instance.disks
|
|
||||||
sa_roles = ["roles/logging.logWriter", "roles/monitoring.metricWriter"]
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "google_service_account" "default" {
|
resource "google_service_account" "default" {
|
||||||
count = var.test_instance == null ? 0 : 1
|
count = var.test_instance == null ? 0 : 1
|
||||||
project = var.test_instance.project_id
|
project = var.test_instance.project_id
|
||||||
account_id = "cos-test-${var.test_instance.name}"
|
account_id = "fabric-container-${var.test_instance.name}"
|
||||||
display_name = "Managed by the cos Terraform module."
|
display_name = "Managed by the cos Terraform module."
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "google_project_iam_member" "default" {
|
resource "google_project_iam_member" "default" {
|
||||||
for_each = var.test_instance == null ? toset([]) : toset(local.sa_roles)
|
for_each = (
|
||||||
project = var.test_instance.project_id
|
var.test_instance == null
|
||||||
role = each.value
|
? toset([])
|
||||||
member = "serviceAccount:${google_service_account.default[0].email}"
|
: toset(var.test_instance_defaults.service_account_roles)
|
||||||
|
)
|
||||||
|
project = var.test_instance.project_id
|
||||||
|
role = each.value
|
||||||
|
member = "serviceAccount:${google_service_account.default[0].email}"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "google_compute_disk" "disks" {
|
resource "google_compute_disk" "disks" {
|
||||||
for_each = local.disks
|
for_each = (
|
||||||
project = var.test_instance.project_id
|
var.test_instance == null
|
||||||
zone = var.test_instance.zone
|
? {}
|
||||||
name = each.key
|
: var.test_instance_defaults.disks
|
||||||
type = "pd-ssd"
|
)
|
||||||
size = each.value.size
|
project = var.test_instance.project_id
|
||||||
|
zone = var.test_instance.zone
|
||||||
|
name = each.key
|
||||||
|
type = "pd-ssd"
|
||||||
|
size = each.value.size
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "google_compute_instance" "default" {
|
resource "google_compute_instance" "default" {
|
||||||
|
@ -48,16 +51,16 @@ resource "google_compute_instance" "default" {
|
||||||
zone = var.test_instance.zone
|
zone = var.test_instance.zone
|
||||||
name = var.test_instance.name
|
name = var.test_instance.name
|
||||||
description = "Managed by the cos Terraform module."
|
description = "Managed by the cos Terraform module."
|
||||||
tags = var.test_instance.tags
|
tags = var.test_instance_defaults.tags
|
||||||
machine_type = (
|
machine_type = (
|
||||||
var.test_instance.type == null ? "f1-micro" : var.test_instance.type
|
var.test_instance.type == null ? "f1-micro" : var.test_instance.type
|
||||||
)
|
)
|
||||||
metadata = merge(var.test_instance.metadata, {
|
metadata = merge(var.test_instance_defaults.metadata, {
|
||||||
user-data = local.cloud_config
|
user-data = local.cloud_config
|
||||||
})
|
})
|
||||||
|
|
||||||
dynamic attached_disk {
|
dynamic attached_disk {
|
||||||
for_each = local.disks
|
for_each = var.test_instance_defaults.disks
|
||||||
iterator = disk
|
iterator = disk
|
||||||
content {
|
content {
|
||||||
device_name = disk.key
|
device_name = disk.key
|
||||||
|
@ -68,15 +71,26 @@ resource "google_compute_instance" "default" {
|
||||||
|
|
||||||
boot_disk {
|
boot_disk {
|
||||||
initialize_params {
|
initialize_params {
|
||||||
type = "pd-ssd"
|
type = "pd-ssd"
|
||||||
image = "projects/cos-cloud/global/images/family/cos-stable"
|
image = (
|
||||||
size = 10
|
var.test_instance_defaults.image == null
|
||||||
|
? "projects/cos-cloud/global/images/family/cos-stable"
|
||||||
|
: var.test_instance_defaults.image
|
||||||
|
)
|
||||||
|
size = 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
network_interface {
|
network_interface {
|
||||||
network = var.test_instance.network
|
network = var.test_instance.network
|
||||||
subnetwork = var.test_instance.subnetwork
|
subnetwork = var.test_instance.subnetwork
|
||||||
|
dynamic access_config {
|
||||||
|
for_each = var.test_instance_defaults.nat ? [""] : []
|
||||||
|
iterator = config
|
||||||
|
content {
|
||||||
|
nat_ip = null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
service_account {
|
service_account {
|
|
@ -68,11 +68,8 @@ module "cos-mysql" {
|
||||||
zone = "europe-west1-b"
|
zone = "europe-west1-b"
|
||||||
name = "cos-mysql"
|
name = "cos-mysql"
|
||||||
type = "n1-standard-1"
|
type = "n1-standard-1"
|
||||||
tags = ["ssh"]
|
|
||||||
metadata = {}
|
|
||||||
network = "default"
|
network = "default"
|
||||||
subnetwork = "https://www.googleapis.com/compute/v1/projects/my-project/regions/europe-west1/subnetworks/my-subnet"
|
subnetwork = "https://www.googleapis.com/compute/v1/projects/my-project/regions/europe-west1/subnetworks/my-subnet"
|
||||||
disks = []
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -89,7 +86,8 @@ module "cos-mysql" {
|
||||||
| *kms_config* | Optional KMS configuration to decrypt passed-in password. Leave null if a plaintext password is used. | <code title="object({ project_id = string keyring = string location = string key = string })">object({...})</code> | | <code title="">null</code> |
|
| *kms_config* | Optional KMS configuration to decrypt passed-in password. Leave null if a plaintext password is used. | <code title="object({ project_id = string keyring = string location = string key = string })">object({...})</code> | | <code title="">null</code> |
|
||||||
| *mysql_config* | MySQL configuration file content, if null container default will be used. | <code title="">string</code> | | <code title="">null</code> |
|
| *mysql_config* | MySQL configuration file content, if null container default will be used. | <code title="">string</code> | | <code title="">null</code> |
|
||||||
| *mysql_data_disk* | MySQL data disk name in /dev/disk/by-id/ including the google- prefix. If null the boot disk will be used for data. | <code title="">string</code> | | <code title="">null</code> |
|
| *mysql_data_disk* | MySQL data disk name in /dev/disk/by-id/ including the google- prefix. If null the boot disk will be used for data. | <code title="">string</code> | | <code title="">null</code> |
|
||||||
| *test_instance* | Test/development instance attributes, leave null to skip creation. | <code title="object({ project_id = string zone = string name = string type = string tags = list(string) metadata = map(string) network = string subnetwork = string disks = map(object({ read_only = bool size = number })) })">object({...})</code> | | <code title="">null</code> |
|
| *test_instance* | Test/development instance attributes, leave null to skip creation. | <code title="object({ project_id = string zone = string name = string type = string network = string subnetwork = string })">object({...})</code> | | <code title="">null</code> |
|
||||||
|
| *test_instance_defaults* | Test/development instance defaults used for optional configuration. | <code title="object({ disks = map(object({ read_only = bool size = number })) metadata = map(string) service_account_roles = list(string) tags = list(string) })">object({...})</code> | | <code title="{ disks = {} metadata = {} service_account_roles = [ "roles/logging.logWriter", "roles/monitoring.metricWriter" ] tags = ["ssh"] }">...</code> |
|
||||||
|
|
||||||
## Outputs
|
## Outputs
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
# Containerized Nginx on Container Optimized OS
|
||||||
|
|
||||||
|
This module manages a `cloud-config` configuration that starts a containerized [Nginx](https://nginx.org/en/) service on Container Optimized OS, using the [hello demo image](https://hub.docker.com/r/nginxdemos/hello/).
|
||||||
|
|
||||||
|
The resulting `cloud-config` can be customized in a number of ways:
|
||||||
|
|
||||||
|
- a custom Nginx configuration can be set in `/etc/nginx/conf.d` using the `nginx_config` variable
|
||||||
|
- additional files (eg for hosts or zone files) 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 port 80.
|
||||||
|
|
||||||
|
Logging and monitoring are enabled via the [Google Cloud Logging driver](https://docs.docker.com/config/containers/logging/gcplogs/) configured for the CoreDNS 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 configuration
|
||||||
|
|
||||||
|
This example will create a `cloud-config` that uses the module's defaults, creating a simple hello web server showing host name and request id.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "cos-nginx" {
|
||||||
|
source = "./modules/cos-container/nginx"
|
||||||
|
}
|
||||||
|
|
||||||
|
# use it as metadata in a compute instance or template
|
||||||
|
resource "google_compute_instance" "default" {
|
||||||
|
metadata = {
|
||||||
|
user-data = module.cos-nginx.cloud_config
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nginx 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-nginx" {
|
||||||
|
source = "./modules/cos-container/nginx"
|
||||||
|
test_instance = {
|
||||||
|
project_id = "my-project"
|
||||||
|
zone = "europe-west1-b"
|
||||||
|
name = "cos-nginx"
|
||||||
|
type = "f1-micro"
|
||||||
|
network = "default"
|
||||||
|
subnetwork = "https://www.googleapis.com/compute/v1/projects/my-project/regions/europe-west1/subnetworks/my-subnet"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- BEGIN TFDOC -->
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
| name | description | type | required | default |
|
||||||
|
|---|---|:---: |:---:|:---:|
|
||||||
|
| *cloud_config* | Cloud config template path. If null default will be used. | <code title="">string</code> | | <code title="">null</code> |
|
||||||
|
| *config_variables* | Additional variables used to render the cloud-config and Nginx templates. | <code title="map(any)">map(any)</code> | | <code title="">{}</code> |
|
||||||
|
| *file_defaults* | Default owner and permissions for files. | <code title="object({ owner = string permissions = string })">object({...})</code> | | <code title="{ owner = "root" permissions = "0644" }">...</code> |
|
||||||
|
| *files* | Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null. | <code title="map(object({ content = string owner = string permissions = string }))">map(object({...}))</code> | | <code title="">{}</code> |
|
||||||
|
| *image* | Nginx container image. | <code title="">string</code> | | <code title="">nginxdemos/hello:plain-text</code> |
|
||||||
|
| *nginx_config* | Nginx configuration path, if null container default will be used. | <code title="">string</code> | | <code title="">null</code> |
|
||||||
|
| *test_instance* | Test/development instance attributes, leave null to skip creation. | <code title="object({ project_id = string zone = string name = string type = string network = string subnetwork = string })">object({...})</code> | | <code title="">null</code> |
|
||||||
|
| *test_instance_defaults* | Test/development instance defaults used for optional configuration. If image is null, COS stable will be used. | <code title="object({ disks = map(object({ read_only = bool size = number })) image = string metadata = map(string) nat = bool service_account_roles = list(string) tags = list(string) })">object({...})</code> | | <code title="{ disks = {} image = null metadata = {} nat = false service_account_roles = [ "roles/logging.logWriter", "roles/monitoring.metricWriter" ] tags = ["ssh"] }">...</code> |
|
||||||
|
|
||||||
|
## 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 | |
|
||||||
|
<!-- END TFDOC -->
|
|
@ -0,0 +1,78 @@
|
||||||
|
#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.
|
||||||
|
|
||||||
|
# https://hub.docker.com/r/nginx/nginx/
|
||||||
|
# https://nginx.io/manual/toc/#installation
|
||||||
|
|
||||||
|
users:
|
||||||
|
- name: nginx
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%{~ if nginx_config != null ~}
|
||||||
|
- path: /etc/nginx/conf.d/nginx.conf
|
||||||
|
permissions: 0644
|
||||||
|
owner: root
|
||||||
|
content: |
|
||||||
|
${indent(6, nginx_config)}
|
||||||
|
%{~ endif ~}
|
||||||
|
|
||||||
|
# nginx container service
|
||||||
|
- path: /etc/systemd/system/nginx.service
|
||||||
|
permissions: 0644
|
||||||
|
owner: root
|
||||||
|
content: |
|
||||||
|
[Unit]
|
||||||
|
Description=Start nginx container
|
||||||
|
After=gcr-online.target docker.socket
|
||||||
|
Wants=gcr-online.target docker.socket docker-events-collector.service
|
||||||
|
[Service]
|
||||||
|
ExecStart=/usr/bin/docker run --rm --name=nginx \
|
||||||
|
--log-driver=gcplogs --network host \
|
||||||
|
%{~ if etc_mount ~}
|
||||||
|
-v /etc/nginx/conf.d:/etc/nginx/conf.d \
|
||||||
|
%{~ endif ~}
|
||||||
|
${image}
|
||||||
|
ExecStop=/usr/bin/docker stop nginx
|
||||||
|
|
||||||
|
%{ 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 80 -m state --state NEW,ESTABLISHED -j ACCEPT
|
||||||
|
- systemctl daemon-reload
|
||||||
|
- systemctl restart systemd-resolved.service
|
||||||
|
- systemctl start nginx
|
|
@ -0,0 +1,101 @@
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
resource "google_service_account" "default" {
|
||||||
|
count = var.test_instance == null ? 0 : 1
|
||||||
|
project = var.test_instance.project_id
|
||||||
|
account_id = "fabric-container-${var.test_instance.name}"
|
||||||
|
display_name = "Managed by the cos Terraform module."
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_project_iam_member" "default" {
|
||||||
|
for_each = (
|
||||||
|
var.test_instance == null
|
||||||
|
? toset([])
|
||||||
|
: toset(var.test_instance_defaults.service_account_roles)
|
||||||
|
)
|
||||||
|
project = var.test_instance.project_id
|
||||||
|
role = each.value
|
||||||
|
member = "serviceAccount:${google_service_account.default[0].email}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_compute_disk" "disks" {
|
||||||
|
for_each = (
|
||||||
|
var.test_instance == null
|
||||||
|
? {}
|
||||||
|
: var.test_instance_defaults.disks
|
||||||
|
)
|
||||||
|
project = var.test_instance.project_id
|
||||||
|
zone = var.test_instance.zone
|
||||||
|
name = each.key
|
||||||
|
type = "pd-ssd"
|
||||||
|
size = each.value.size
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_compute_instance" "default" {
|
||||||
|
count = var.test_instance == null ? 0 : 1
|
||||||
|
project = var.test_instance.project_id
|
||||||
|
zone = var.test_instance.zone
|
||||||
|
name = var.test_instance.name
|
||||||
|
description = "Managed by the cos Terraform module."
|
||||||
|
tags = var.test_instance_defaults.tags
|
||||||
|
machine_type = (
|
||||||
|
var.test_instance.type == null ? "f1-micro" : var.test_instance.type
|
||||||
|
)
|
||||||
|
metadata = merge(var.test_instance_defaults.metadata, {
|
||||||
|
user-data = local.cloud_config
|
||||||
|
})
|
||||||
|
|
||||||
|
dynamic attached_disk {
|
||||||
|
for_each = var.test_instance_defaults.disks
|
||||||
|
iterator = disk
|
||||||
|
content {
|
||||||
|
device_name = disk.key
|
||||||
|
mode = disk.value.read_only ? "READ_ONLY" : "READ_WRITE"
|
||||||
|
source = google_compute_disk.disks[disk.key].name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boot_disk {
|
||||||
|
initialize_params {
|
||||||
|
type = "pd-ssd"
|
||||||
|
image = (
|
||||||
|
var.test_instance_defaults.image == null
|
||||||
|
? "projects/cos-cloud/global/images/family/cos-stable"
|
||||||
|
: var.test_instance_defaults.image
|
||||||
|
)
|
||||||
|
size = 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
network_interface {
|
||||||
|
network = var.test_instance.network
|
||||||
|
subnetwork = var.test_instance.subnetwork
|
||||||
|
dynamic access_config {
|
||||||
|
for_each = var.test_instance_defaults.nat ? [""] : []
|
||||||
|
iterator = config
|
||||||
|
content {
|
||||||
|
nat_ip = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
service_account {
|
||||||
|
email = google_service_account.default[0].email
|
||||||
|
scopes = ["https://www.googleapis.com/auth/cloud-platform"]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
* 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(var.config_variables, {
|
||||||
|
etc_mount = (
|
||||||
|
var.nginx_config != null || length([
|
||||||
|
for name in keys(var.files) :
|
||||||
|
name if substr(name, 0, 18) == "/etc/nginx/conf.d/"
|
||||||
|
]) > 1
|
||||||
|
)
|
||||||
|
files = local.files
|
||||||
|
image = var.image
|
||||||
|
nginx_config = (var.nginx_config == null ? null : templatefile(
|
||||||
|
var.nginx_config, var.config_variables
|
||||||
|
))
|
||||||
|
}))
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
|
@ -17,8 +17,12 @@
|
||||||
output "test_instance" {
|
output "test_instance" {
|
||||||
description = "Optional test instance name and address"
|
description = "Optional test instance name and address"
|
||||||
value = (var.test_instance == null ? {} : {
|
value = (var.test_instance == null ? {} : {
|
||||||
address = google_compute_instance.default[0].network_interface.0.network_ip
|
address = google_compute_instance.default[0].network_interface.0.network_ip
|
||||||
name = google_compute_instance.default[0].name
|
name = google_compute_instance.default[0].name
|
||||||
|
nat_address = try(
|
||||||
|
google_compute_instance.default[0].network_interface.0.access_config.0.nat_ip,
|
||||||
|
null
|
||||||
|
)
|
||||||
service_account = google_service_account.default[0].email
|
service_account = google_service_account.default[0].email
|
||||||
})
|
})
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -21,14 +21,34 @@ variable "test_instance" {
|
||||||
zone = string
|
zone = string
|
||||||
name = string
|
name = string
|
||||||
type = string
|
type = string
|
||||||
tags = list(string)
|
|
||||||
metadata = map(string)
|
|
||||||
network = string
|
network = string
|
||||||
subnetwork = string
|
subnetwork = string
|
||||||
|
})
|
||||||
|
default = null
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "test_instance_defaults" {
|
||||||
|
description = "Test/development instance defaults used for optional configuration. If image is null, COS stable will be used."
|
||||||
|
type = object({
|
||||||
disks = map(object({
|
disks = map(object({
|
||||||
read_only = bool
|
read_only = bool
|
||||||
size = number
|
size = number
|
||||||
}))
|
}))
|
||||||
|
image = string
|
||||||
|
metadata = map(string)
|
||||||
|
nat = bool
|
||||||
|
service_account_roles = list(string)
|
||||||
|
tags = list(string)
|
||||||
})
|
})
|
||||||
default = null
|
default = {
|
||||||
|
disks = {}
|
||||||
|
image = null
|
||||||
|
metadata = {}
|
||||||
|
nat = false
|
||||||
|
service_account_roles = [
|
||||||
|
"roles/logging.logWriter",
|
||||||
|
"roles/monitoring.metricWriter"
|
||||||
|
]
|
||||||
|
tags = ["ssh"]
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/**
|
||||||
|
* 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 Nginx templates."
|
||||||
|
type = map(any)
|
||||||
|
default = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "image" {
|
||||||
|
description = "Nginx container image."
|
||||||
|
type = string
|
||||||
|
default = "nginxdemos/hello:plain-text"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "nginx_config" {
|
||||||
|
description = "Nginx configuration path, if null container 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 = {}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
. {
|
||||||
|
hosts /etc/coredns/onprem.hosts onprem.example.org {
|
||||||
|
127.0.0.1 localhost.example.org localhost
|
||||||
|
}
|
||||||
|
forward . /etc/resolv.conf
|
||||||
|
reload
|
||||||
|
log
|
||||||
|
errors
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
# Containerized on-premises infrastructure
|
||||||
|
|
||||||
|
This module manages a `cloud-config` configuration that starts an emulated on-premises infrastructure running in Docker Compose on a single instance, and connects it via static or dynamic VPN to a Google Cloud VPN gateway.
|
||||||
|
|
||||||
|
The emulated on-premises infrastructure is composed of:
|
||||||
|
|
||||||
|
- a Strongswan container managing the VPN tunnel to GCP
|
||||||
|
- an optional Bird container managing the BGP session
|
||||||
|
- a CoreDNS container servng local DNS and forwarding to GCP
|
||||||
|
- an Nginx container serving a simple static web page
|
||||||
|
- a generic Linux container used as a jump host inside the on-premises network
|
||||||
|
|
||||||
|
A [complete scenario using this module](../../../infrastructure/onprem-google-access-dns) is available in the infrastructure examples.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
### Static VPN
|
||||||
|
|
||||||
|
The test instance is optional, as described above.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "cloud-vpn" {
|
||||||
|
source = "./modules/net-vpn-static"
|
||||||
|
project_id = "my-project"
|
||||||
|
region = "europe-west1"
|
||||||
|
network = "my-vpc"
|
||||||
|
name = "to-on-prem"
|
||||||
|
remote_ranges = ["192.168.192.0/24"]
|
||||||
|
tunnels = {
|
||||||
|
remote-0 = {
|
||||||
|
ike_version = 2
|
||||||
|
peer_ip = module.on-prem.external_address
|
||||||
|
shared_secret = ""
|
||||||
|
traffic_selectors = { local = ["0.0.0.0/0"], remote = null }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module "on-prem" {
|
||||||
|
source = "./modules/cos-container/on-prem"
|
||||||
|
name = "onprem"
|
||||||
|
vpn_config = {
|
||||||
|
type = "static"
|
||||||
|
peer_ip = module.cloud-vpn.address
|
||||||
|
shared_secret = module.cloud-vpn.random_secret
|
||||||
|
}
|
||||||
|
test_instance = {
|
||||||
|
project_id = "my-project"
|
||||||
|
zone = "europe-west1-b"
|
||||||
|
name = "cos-coredns"
|
||||||
|
type = "f1-micro"
|
||||||
|
network = "default"
|
||||||
|
subnetwork = "https://www.googleapis.com/compute/v1/projects/my-project/regions/europe-west1/subnetworks/my-subnet"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- BEGIN TFDOC -->
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
| name | description | type | required | default |
|
||||||
|
|---|---|:---: |:---:|:---:|
|
||||||
|
| vpn_config | VPN configuration, type must be one of 'dynamic' or 'static'. | <code title="object({ peer_ip = string shared_secret = string type = string })">object({...})</code> | ✓ | |
|
||||||
|
| *coredns_config* | CoreDNS configuration path, if null default will be used. | <code title="">string</code> | | <code title="">null</code> |
|
||||||
|
| *local_ip_cidr_range* | IP CIDR range used for the Docker onprem network. | <code title="">string</code> | | <code title="">192.168.192.0/24</code> |
|
||||||
|
| *test_instance* | Test/development instance attributes, leave null to skip creation. | <code title="object({ project_id = string zone = string name = string type = string network = string subnetwork = string })">object({...})</code> | | <code title="">null</code> |
|
||||||
|
| *test_instance_defaults* | Test/development instance defaults used for optional configuration. | <code title="object({ disks = map(object({ read_only = bool size = number })) metadata = map(string) service_account_roles = list(string) tags = list(string) })">object({...})</code> | | <code title="{ disks = {} metadata = {} service_account_roles = [ "roles/logging.logWriter", "roles/monitoring.metricWriter" ] tags = ["ssh"] }">...</code> |
|
||||||
|
| *vpn_dynamic_config* | BGP configuration for dynamic VPN, ignored if VPN type is 'static'. | <code title="object({ local_bgp_asn = number local_bgp_address = string peer_bgp_asn = number peer_bgp_address = string })">object({...})</code> | | <code title="{ local_bgp_asn = 65002 local_bgp_address = "169.254.0.2" peer_bgp_asn = 65001 peer_bgp_address = "169.254.0.1" }">...</code> |
|
||||||
|
| *vpn_static_ranges* | Remote CIDR ranges for static VPN, ignored if VPN type is 'dynamic'. | <code title="list(string)">list(string)</code> | | <code title="">["10.0.0.0/8"]</code> |
|
||||||
|
|
||||||
|
## 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 | |
|
||||||
|
<!-- END TFDOC -->
|
|
@ -65,11 +65,13 @@ write_files:
|
||||||
image: gcr.io/pso-cft-fabric/strongswan:latest
|
image: gcr.io/pso-cft-fabric/strongswan:latest
|
||||||
networks:
|
networks:
|
||||||
onprem:
|
onprem:
|
||||||
ipv4_address: ${vpn_ip_address}
|
ipv4_address: ${local_addresses.vpn}
|
||||||
ports:
|
ports:
|
||||||
- "500:500/udp"
|
- "500:500/udp"
|
||||||
- "4500:4500/udp"
|
- "4500:4500/udp"
|
||||||
|
%{~ if vpn_config.type == "dynamic" ~}
|
||||||
- "179:179/tcp"
|
- "179:179/tcp"
|
||||||
|
%{~ endif ~}
|
||||||
privileged: true
|
privileged: true
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
|
@ -78,9 +80,12 @@ write_files:
|
||||||
- "/etc/localtime:/etc/localtime:ro"
|
- "/etc/localtime:/etc/localtime:ro"
|
||||||
- "/var/lib/docker-compose/onprem/ipsec/ipsec.conf:/etc/ipsec.conf:ro"
|
- "/var/lib/docker-compose/onprem/ipsec/ipsec.conf:/etc/ipsec.conf:ro"
|
||||||
- "/var/lib/docker-compose/onprem/ipsec/ipsec.secrets:/etc/ipsec.secrets:ro"
|
- "/var/lib/docker-compose/onprem/ipsec/ipsec.secrets:/etc/ipsec.secrets:ro"
|
||||||
|
%{~ if vpn_config.type == "dynamic" ~}
|
||||||
- "/var/lib/docker-compose/onprem/ipsec/vti.conf:/etc/strongswan.d/vti.conf:ro"
|
- "/var/lib/docker-compose/onprem/ipsec/vti.conf:/etc/strongswan.d/vti.conf:ro"
|
||||||
|
%{~ endif ~}
|
||||||
environment:
|
environment:
|
||||||
- LAN_NETWORKS=${local_ip_cidr_range}
|
- LAN_NETWORKS=${ip_cidr_ranges.local}
|
||||||
|
%{~ if vpn_config.type == "dynamic" ~}
|
||||||
bird:
|
bird:
|
||||||
image: pierky/bird
|
image: pierky/bird
|
||||||
network_mode: service:vpn
|
network_mode: service:vpn
|
||||||
|
@ -91,15 +96,18 @@ write_files:
|
||||||
privileged: true
|
privileged: true
|
||||||
volumes:
|
volumes:
|
||||||
- "/var/lib/docker-compose/onprem/bird/bird.conf:/etc/bird/bird.conf:ro"
|
- "/var/lib/docker-compose/onprem/bird/bird.conf:/etc/bird/bird.conf:ro"
|
||||||
|
%{~ endif ~}
|
||||||
dns:
|
dns:
|
||||||
image: coredns/coredns
|
image: coredns/coredns
|
||||||
command: "-conf /etc/coredns/Corefile"
|
command: "-conf /etc/coredns/Corefile"
|
||||||
depends_on:
|
depends_on:
|
||||||
- "vpn"
|
- "vpn"
|
||||||
|
%{~ if vpn_config.type == "dynamic" ~}
|
||||||
- "bird"
|
- "bird"
|
||||||
|
%{~ endif ~}
|
||||||
networks:
|
networks:
|
||||||
onprem:
|
onprem:
|
||||||
ipv4_address: ${dns_ip_address}
|
ipv4_address: ${local_addresses.dns}
|
||||||
volumes:
|
volumes:
|
||||||
- "/var/lib/docker-compose/onprem/coredns:/etc/coredns:ro"
|
- "/var/lib/docker-compose/onprem/coredns:/etc/coredns:ro"
|
||||||
routing_sidecar_dns:
|
routing_sidecar_dns:
|
||||||
|
@ -108,19 +116,21 @@ write_files:
|
||||||
command: |
|
command: |
|
||||||
/bin/sh -c "\
|
/bin/sh -c "\
|
||||||
ip route del default &&\
|
ip route del default &&\
|
||||||
ip route add default via ${vpn_ip_address}"
|
ip route add default via ${local_addresses.vpn}"
|
||||||
privileged: true
|
privileged: true
|
||||||
web:
|
web:
|
||||||
image: nginx:stable-alpine
|
image: nginx:stable-alpine
|
||||||
depends_on:
|
depends_on:
|
||||||
- "vpn"
|
- "vpn"
|
||||||
|
%{~ if vpn_config.type == "dynamic" ~}
|
||||||
- "bird"
|
- "bird"
|
||||||
|
%{~ endif ~}
|
||||||
- "dns"
|
- "dns"
|
||||||
dns:
|
dns:
|
||||||
- ${dns_ip_address}
|
- ${local_addresses.dns}
|
||||||
networks:
|
networks:
|
||||||
onprem:
|
onprem:
|
||||||
ipv4_address: ${web_ip_address}
|
ipv4_address: ${local_addresses.www}
|
||||||
volumes:
|
volumes:
|
||||||
- "/var/lib/docker-compose/onprem/nginx:/usr/share/nginx/html:ro"
|
- "/var/lib/docker-compose/onprem/nginx:/usr/share/nginx/html:ro"
|
||||||
routing_sidecar_web:
|
routing_sidecar_web:
|
||||||
|
@ -129,40 +139,40 @@ write_files:
|
||||||
command: |
|
command: |
|
||||||
/bin/sh -c "\
|
/bin/sh -c "\
|
||||||
ip route del default &&\
|
ip route del default &&\
|
||||||
ip route add default via ${vpn_ip_address}"
|
ip route add default via ${local_addresses.vpn}"
|
||||||
privileged: true
|
privileged: true
|
||||||
toolbox:
|
toolbox:
|
||||||
image: gcr.io/pso-cft-fabric/toolbox:latest
|
image: gcr.io/pso-cft-fabric/toolbox:latest
|
||||||
networks:
|
networks:
|
||||||
onprem:
|
onprem:
|
||||||
ipv4_address: ${toolbox_ip_address}
|
ipv4_address: ${local_addresses.shell}
|
||||||
depends_on:
|
depends_on:
|
||||||
- "vpn"
|
- "vpn"
|
||||||
- "dns"
|
- "dns"
|
||||||
- "web"
|
- "web"
|
||||||
dns:
|
dns:
|
||||||
- ${dns_ip_address}
|
- ${local_addresses.dns}
|
||||||
routing_sidecar_toolbox:
|
routing_sidecar_toolbox:
|
||||||
image: alpine
|
image: alpine
|
||||||
network_mode: service:toolbox
|
network_mode: service:toolbox
|
||||||
command: |
|
command: |
|
||||||
/bin/sh -c "\
|
/bin/sh -c "\
|
||||||
ip route del default &&\
|
ip route del default &&\
|
||||||
ip route add default via ${vpn_ip_address}"
|
ip route add default via ${local_addresses.vpn}"
|
||||||
privileged: true
|
privileged: true
|
||||||
networks:
|
networks:
|
||||||
onprem:
|
onprem:
|
||||||
ipam:
|
ipam:
|
||||||
driver: default
|
driver: default
|
||||||
config:
|
config:
|
||||||
- subnet: ${local_ip_cidr_range}
|
- subnet: ${ip_cidr_ranges.local}
|
||||||
|
|
||||||
# IPSEC tunnel secret
|
# IPSEC tunnel secret
|
||||||
- path: /var/lib/docker-compose/onprem/ipsec/ipsec.secrets
|
- path: /var/lib/docker-compose/onprem/ipsec/ipsec.secrets
|
||||||
owner: root:root
|
owner: root:root
|
||||||
permissions: '0600'
|
permissions: '0600'
|
||||||
content: |
|
content: |
|
||||||
: PSK "${shared_secret}"
|
: PSK "${vpn_config.shared_secret}"
|
||||||
|
|
||||||
# IPSEC tunnel configuration
|
# IPSEC tunnel configuration
|
||||||
- path: /var/lib/docker-compose/onprem/ipsec/ipsec.conf
|
- path: /var/lib/docker-compose/onprem/ipsec/ipsec.conf
|
||||||
|
@ -181,19 +191,30 @@ write_files:
|
||||||
authby=psk
|
authby=psk
|
||||||
|
|
||||||
conn gcp
|
conn gcp
|
||||||
leftupdown="/var/lib/strongswan/ipsec-vti.sh 0 ${peer_bgp_address}/30 ${local_bgp_address}/30"
|
%{~ if vpn_config.type == "dynamic" ~}
|
||||||
|
leftupdown="/var/lib/strongswan/ipsec-vti.sh 0 ${vpn_dynamic_config.peer_bgp_address}/30 ${vpn_dynamic_config.local_bgp_address}/30"
|
||||||
|
%{~ endif ~}
|
||||||
left=%any
|
left=%any
|
||||||
leftid=%any
|
leftid=%any
|
||||||
|
%{~ if vpn_config.type == "dynamic" ~}
|
||||||
leftsubnet=0.0.0.0/0
|
leftsubnet=0.0.0.0/0
|
||||||
|
%{~ else ~}
|
||||||
|
leftsubnet=${ip_cidr_ranges.local}
|
||||||
|
%{~ endif ~}
|
||||||
leftauth=psk
|
leftauth=psk
|
||||||
right=${peer_ip_wildcard}
|
right=${vpn_config.peer_ip_wildcard}
|
||||||
rightid=${peer_ip}
|
rightid=${vpn_config.peer_ip}
|
||||||
|
%{~ if vpn_config.type == "dynamic" ~}
|
||||||
rightsubnet=0.0.0.0/0
|
rightsubnet=0.0.0.0/0
|
||||||
|
%{~ else ~}
|
||||||
|
rightsubnet=${ip_cidr_ranges.remote}
|
||||||
|
%{~ endif ~}
|
||||||
rightauth=psk
|
rightauth=psk
|
||||||
type=tunnel
|
type=tunnel
|
||||||
auto=start
|
auto=start
|
||||||
dpdaction=restart
|
dpdaction=restart
|
||||||
closeaction=restart
|
closeaction=restart
|
||||||
|
%{~ if vpn_config.type == "dynamic" ~}
|
||||||
mark=%unique
|
mark=%unique
|
||||||
|
|
||||||
# Charon configuration
|
# Charon configuration
|
||||||
|
@ -210,70 +231,70 @@ write_files:
|
||||||
owner: root:root
|
owner: root:root
|
||||||
permissions: '0644'
|
permissions: '0644'
|
||||||
content: |
|
content: |
|
||||||
router id ${local_bgp_address};
|
router id ${vpn_dynamic_config.local_bgp_address};
|
||||||
|
|
||||||
# Watch interface up/down events
|
# watch interface up/down events
|
||||||
protocol device {
|
protocol device {
|
||||||
scan time 10;
|
scan time 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Sync routes to kernel
|
# sync routes to kernel
|
||||||
protocol kernel {
|
protocol kernel {
|
||||||
learn;
|
learn;
|
||||||
merge paths on; # For ECMP
|
merge paths on; # For ECMP
|
||||||
export filter {
|
export filter {
|
||||||
krt_prefsrc = ${vpn_ip_address}; # Internal IP Address of the strongSwan VM.
|
# internal IP of the strongswan VM
|
||||||
accept; # Sync all routes to kernel
|
krt_prefsrc = ${local_addresses.vpn};
|
||||||
};
|
# sync all routes to kernel
|
||||||
import all; # Required due to /32 on GCE VMs for the static route below
|
accept;
|
||||||
|
};
|
||||||
|
import all; # Required due to /32 on GCE VMs for the static route below
|
||||||
}
|
}
|
||||||
|
|
||||||
# Configure a static route to make sure route exists
|
# Configure a static route to make sure route exists
|
||||||
protocol static {
|
protocol static {
|
||||||
# Network connected to eth0
|
# network connected to eth0
|
||||||
route ${local_ip_cidr_range} recursive ${local_gw_ip};
|
route ${ip_cidr_ranges.local} recursive ${local_addresses.gw};
|
||||||
# Private google access
|
%{~ for range in netblocks ~}
|
||||||
route 199.36.153.4/30 via ${peer_bgp_address};
|
# route ${range} via ${vpn_dynamic_config.peer_bgp_address};
|
||||||
# Cloud DNS forwarding zone
|
%{~ endfor ~}
|
||||||
route 35.199.192.0/19 via ${peer_bgp_address};
|
|
||||||
}
|
}
|
||||||
# Prefix lists for routing security
|
# prefix lists for routing security
|
||||||
# Allow any possible GCP Subnet
|
# allow any possible GCP Subnet
|
||||||
define GCP_VPC_A_PREFIXES = [ 10.0.0.0/8{9,29}, 172.16.0.0/12{12,29}, 192.168.0.0/16{16,29} ];
|
define GCP_VPC_A_PREFIXES = [ 10.0.0.0/8{8,29}, 172.16.0.0/12{12,29}, 192.168.0.0/16{16,29} ];
|
||||||
define LOCAL_PREFIXES = [ ${local_ip_cidr_range} ];
|
define GCP_NETBLOCKS = [ ${join(", ", netblocks)} ];
|
||||||
|
define LOCAL_PREFIXES = [ ${ip_cidr_ranges.local} ];
|
||||||
|
|
||||||
# Filter received prefixes
|
# filter received prefixes
|
||||||
filter gcp_vpc_a_in
|
filter gcp_vpc_a_in {
|
||||||
{
|
if (net ~ GCP_VPC_A_PREFIXES || net ~ GCP_NETBLOCKS) then accept;
|
||||||
if (net ~ GCP_VPC_A_PREFIXES) then accept;
|
else reject;
|
||||||
else reject;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Filter advertised prefixes
|
# filter advertised prefixes
|
||||||
filter gcp_vpc_a_out
|
filter gcp_vpc_a_out {
|
||||||
{
|
if (net ~ LOCAL_PREFIXES) then accept;
|
||||||
if (net ~ LOCAL_PREFIXES) then accept;
|
else reject;
|
||||||
else reject;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template bgp gcp_vpc_a {
|
template bgp gcp_vpc_a {
|
||||||
keepalive time 20;
|
keepalive time 20;
|
||||||
hold time 60;
|
hold time 60;
|
||||||
graceful restart aware; # Cloud Router uses GR during maintenance
|
# Cloud Router uses GR during maintenance
|
||||||
|
graceful restart aware;
|
||||||
import filter gcp_vpc_a_in;
|
import filter gcp_vpc_a_in;
|
||||||
import limit 10 action warn; # restart | block | disable
|
import limit 10 action warn; # restart | block | disable
|
||||||
|
export filter gcp_vpc_a_out;
|
||||||
export filter gcp_vpc_a_out;
|
export limit 10 action warn; # restart | block | disable
|
||||||
export limit 10 action warn; # restart | block | disable
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol bgp gcp_vpc_a_tun1 from gcp_vpc_a
|
protocol bgp gcp_vpc_a_tun1 from gcp_vpc_a {
|
||||||
{
|
local ${vpn_dynamic_config.local_bgp_address} as ${vpn_dynamic_config.local_bgp_asn};
|
||||||
local ${local_bgp_address} as ${local_bgp_asn};
|
neighbor ${vpn_dynamic_config.peer_bgp_address} as ${vpn_dynamic_config.peer_bgp_asn};
|
||||||
neighbor ${peer_bgp_address} as ${peer_bgp_asn};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%{~ endif ~}
|
||||||
|
|
||||||
# CoreDNS configuration
|
# CoreDNS configuration
|
||||||
- path: /var/lib/docker-compose/onprem/coredns/Corefile
|
- path: /var/lib/docker-compose/onprem/coredns/Corefile
|
||||||
owner: root:root
|
owner: root:root
|
||||||
|
@ -286,10 +307,9 @@ write_files:
|
||||||
owner: root:root
|
owner: root:root
|
||||||
permissions: '0644'
|
permissions: '0644'
|
||||||
content: |
|
content: |
|
||||||
${vpn_ip_address} gw.${dns_domain}
|
%{~ for name, address in local_addresses ~}
|
||||||
${dns_ip_address} ns.${dns_domain}
|
${address} ${name}.onprem.example.org
|
||||||
${web_ip_address} www.${dns_domain}
|
%{~ endfor ~}
|
||||||
${toolbox_ip_address} toolbox.${dns_domain}
|
|
||||||
|
|
||||||
# Minimal nginx index page
|
# Minimal nginx index page
|
||||||
- path: /var/lib/docker-compose/onprem/nginx/index.html
|
- path: /var/lib/docker-compose/onprem/nginx/index.html
|
||||||
|
@ -301,7 +321,7 @@ write_files:
|
||||||
<head><meta charset="utf-8"></head>
|
<head><meta charset="utf-8"></head>
|
||||||
<body>
|
<body>
|
||||||
<h1>On Prem in a Box</h1>
|
<h1>On Prem in a Box</h1>
|
||||||
<p>${instance_name}</p>
|
<p>onprem</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
../instance.tf
|
|
@ -0,0 +1,66 @@
|
||||||
|
/**
|
||||||
|
* 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(
|
||||||
|
"${path.module}/cloud-config.yaml",
|
||||||
|
merge(local.cloud_config_vars, var.config_variables)
|
||||||
|
)
|
||||||
|
corefile = (
|
||||||
|
var.coredns_config == null ?
|
||||||
|
"${path.module}/Corefile"
|
||||||
|
: var.coredns_config
|
||||||
|
)
|
||||||
|
cloud_config_vars = {
|
||||||
|
coredns_config = indent(4, templatefile(local.corefile, var.config_variables))
|
||||||
|
ip_cidr_ranges = {
|
||||||
|
local = var.local_ip_cidr_range
|
||||||
|
remote = join(",", concat(
|
||||||
|
var.vpn_static_ranges, local.netblocks
|
||||||
|
))
|
||||||
|
}
|
||||||
|
local_addresses = {
|
||||||
|
gw = cidrhost(var.local_ip_cidr_range, 1)
|
||||||
|
vpn = cidrhost(var.local_ip_cidr_range, 2)
|
||||||
|
dns = cidrhost(var.local_ip_cidr_range, 3)
|
||||||
|
www = cidrhost(var.local_ip_cidr_range, 4)
|
||||||
|
shell = cidrhost(var.local_ip_cidr_range, 5)
|
||||||
|
}
|
||||||
|
netblocks = local.netblocks
|
||||||
|
vpn_config = local.vpn_config
|
||||||
|
vpn_dynamic_config = var.vpn_dynamic_config
|
||||||
|
}
|
||||||
|
netblocks = concat(
|
||||||
|
data.google_netblock_ip_ranges.dns-forwarders.cidr_blocks_ipv4,
|
||||||
|
data.google_netblock_ip_ranges.private-googleapis.cidr_blocks_ipv4,
|
||||||
|
data.google_netblock_ip_ranges.restricted-googleapis.cidr_blocks_ipv4
|
||||||
|
)
|
||||||
|
vpn_config = merge(var.vpn_config, {
|
||||||
|
peer_ip_wildcard = "%${var.vpn_config.peer_ip}"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
data "google_netblock_ip_ranges" "dns-forwarders" {
|
||||||
|
range_type = "dns-forwarders"
|
||||||
|
}
|
||||||
|
|
||||||
|
data "google_netblock_ip_ranges" "private-googleapis" {
|
||||||
|
range_type = "private-googleapis"
|
||||||
|
}
|
||||||
|
|
||||||
|
data "google_netblock_ip_ranges" "restricted-googleapis" {
|
||||||
|
range_type = "restricted-googleapis"
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
../outputs-instance.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
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
../variables-instance.tf
|
|
@ -14,16 +14,16 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
variable "coredns_config" {
|
variable "config_variables" {
|
||||||
description = "CoreDNS configuration, set to null to use default."
|
description = "Additional variables used to render the cloud-config and CoreDNS templates."
|
||||||
type = string
|
type = map(any)
|
||||||
default = null
|
default = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "dns_domain" {
|
variable "coredns_config" {
|
||||||
description = "DNS domain used for on-prem host records."
|
description = "CoreDNS configuration path, if null default will be used."
|
||||||
type = string
|
type = string
|
||||||
default = "onprem.example.com"
|
default = null
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "local_ip_cidr_range" {
|
variable "local_ip_cidr_range" {
|
||||||
|
@ -32,55 +32,6 @@ variable "local_ip_cidr_range" {
|
||||||
default = "192.168.192.0/24"
|
default = "192.168.192.0/24"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "machine_type" {
|
|
||||||
description = "Machine type."
|
|
||||||
type = string
|
|
||||||
default = "g1-small"
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "name" {
|
|
||||||
description = "On-prem-in-a-box compute instance name."
|
|
||||||
type = string
|
|
||||||
default = "onprem"
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "network" {
|
|
||||||
description = "VPC network name."
|
|
||||||
type = string
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "network_tags" {
|
|
||||||
description = "Network tags."
|
|
||||||
type = list(string)
|
|
||||||
default = ["ssh"]
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "project_id" {
|
|
||||||
description = "Project id."
|
|
||||||
type = string
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "service_account" {
|
|
||||||
description = "Service account customization."
|
|
||||||
type = object({
|
|
||||||
email = string
|
|
||||||
scopes = list(string)
|
|
||||||
})
|
|
||||||
default = {
|
|
||||||
email = null
|
|
||||||
scopes = [
|
|
||||||
"https://www.googleapis.com/auth/devstorage.read_only",
|
|
||||||
"https://www.googleapis.com/auth/logging.write",
|
|
||||||
"https://www.googleapis.com/auth/monitoring.write"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "subnet_self_link" {
|
|
||||||
description = "VPC subnet self link."
|
|
||||||
type = string
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "vpn_config" {
|
variable "vpn_config" {
|
||||||
description = "VPN configuration, type must be one of 'dynamic' or 'static'."
|
description = "VPN configuration, type must be one of 'dynamic' or 'static'."
|
||||||
type = object({
|
type = object({
|
||||||
|
@ -109,10 +60,5 @@ variable "vpn_dynamic_config" {
|
||||||
variable "vpn_static_ranges" {
|
variable "vpn_static_ranges" {
|
||||||
description = "Remote CIDR ranges for static VPN, ignored if VPN type is 'dynamic'."
|
description = "Remote CIDR ranges for static VPN, ignored if VPN type is 'dynamic'."
|
||||||
type = list(string)
|
type = list(string)
|
||||||
default = []
|
default = ["10.0.0.0/8"]
|
||||||
}
|
|
||||||
|
|
||||||
variable "zone" {
|
|
||||||
description = "Compute zone."
|
|
||||||
type = string
|
|
||||||
}
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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 "test_instance" {
|
||||||
|
description = "Optional test instance name and address"
|
||||||
|
value = (var.test_instance == null ? {} : {
|
||||||
|
address = google_compute_instance.default[0].network_interface.0.network_ip
|
||||||
|
name = google_compute_instance.default[0].name
|
||||||
|
nat_address = try(
|
||||||
|
google_compute_instance.default[0].network_interface.0.access_config.0.nat_ip,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
service_account = google_service_account.default[0].email
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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 "test_instance" {
|
||||||
|
description = "Test/development instance attributes, leave null to skip creation."
|
||||||
|
type = object({
|
||||||
|
project_id = string
|
||||||
|
zone = string
|
||||||
|
name = string
|
||||||
|
type = string
|
||||||
|
network = string
|
||||||
|
subnetwork = string
|
||||||
|
})
|
||||||
|
default = null
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "test_instance_defaults" {
|
||||||
|
description = "Test/development instance defaults used for optional configuration. If image is null, COS stable will be used."
|
||||||
|
type = object({
|
||||||
|
disks = map(object({
|
||||||
|
read_only = bool
|
||||||
|
size = number
|
||||||
|
}))
|
||||||
|
image = string
|
||||||
|
metadata = map(string)
|
||||||
|
nat = bool
|
||||||
|
service_account_roles = list(string)
|
||||||
|
tags = list(string)
|
||||||
|
})
|
||||||
|
default = {
|
||||||
|
disks = {}
|
||||||
|
image = null
|
||||||
|
metadata = {}
|
||||||
|
nat = false
|
||||||
|
service_account_roles = [
|
||||||
|
"roles/logging.logWriter",
|
||||||
|
"roles/monitoring.metricWriter"
|
||||||
|
]
|
||||||
|
tags = ["ssh"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ output "external_ips" {
|
||||||
var.network_interfaces[0].nat
|
var.network_interfaces[0].nat
|
||||||
? [
|
? [
|
||||||
for name, instance in google_compute_instance.default :
|
for name, instance in google_compute_instance.default :
|
||||||
instance.network_interface.0.network_ip
|
try(instance.network_interface.0.access_config.0.nat_ip, null)
|
||||||
]
|
]
|
||||||
: []
|
: []
|
||||||
)
|
)
|
||||||
|
|
|
@ -31,7 +31,7 @@ module "folders-unit" {
|
||||||
| automation_project_id | Project id used for automation service accounts. | <code title="">string</code> | ✓ | |
|
| automation_project_id | Project id used for automation service accounts. | <code title="">string</code> | ✓ | |
|
||||||
| billing_account_id | Country billing account account. | <code title="">string</code> | ✓ | |
|
| billing_account_id | Country billing account account. | <code title="">string</code> | ✓ | |
|
||||||
| name | Top folder name. | <code title="">string</code> | ✓ | |
|
| name | Top folder name. | <code title="">string</code> | ✓ | |
|
||||||
| organization_id | Organization id. | <code title="">string</code> | ✓ | |
|
| organization_id | Organization id in organizations/nnnnnn format. | <code title="">string</code> | ✓ | |
|
||||||
| root_node | Root node in folders/folder_id or organizations/org_id format. | <code title="">string</code> | ✓ | |
|
| root_node | Root node in folders/folder_id or organizations/org_id format. | <code title="">string</code> | ✓ | |
|
||||||
| short_name | Short name used as GCS bucket and service account prefixes, do not use capital letters or spaces. | <code title="">string</code> | ✓ | |
|
| short_name | Short name used as GCS bucket and service account prefixes, do not use capital letters or spaces. | <code title="">string</code> | ✓ | |
|
||||||
| *environments* | Unit environments short names. | <code title="map(string)">map(string)</code> | | <code title="{ non-prod = "Non production" prod = "Production" }">...</code> |
|
| *environments* | Unit environments short names. | <code title="map(string)">map(string)</code> | | <code title="{ non-prod = "Non production" prod = "Production" }">...</code> |
|
||||||
|
|
|
@ -1,131 +0,0 @@
|
||||||
# On-prem-in-a-box Module
|
|
||||||
|
|
||||||
This module allows emulating an on-premise enviroment in a single GCE VM, by connecting a Docker Network to a VPC via a static or dynamic (BGP) VPN connection implemented with Strongswan. It provides a good playground for testing private access and hybrid DNS connectivity between on-premise and Google Cloud.
|
|
||||||
|
|
||||||
To see this module in action, please refer to the folowing end-to-end network examples:
|
|
||||||
- [hub-and-spoke-peerings](../../infrastructure/hub-and-spoke-peerings/)
|
|
||||||
|
|
||||||
## TODO
|
|
||||||
|
|
||||||
- [ ] describe how to check and troubleshoot the onprem VPN and services
|
|
||||||
- [ ] add support for service account, scopes and network tags
|
|
||||||
- [ ] allow passing in arbitrary CoreDNS configurations instead of tweaking a default one via variables
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Static VPN Gateway
|
|
||||||
```hcl
|
|
||||||
module "cloud-vpn" {
|
|
||||||
source = "modules/net-vpn-static/"
|
|
||||||
project_id = "<PROJECT_ID>"
|
|
||||||
region = "europe-west4"
|
|
||||||
network = "vpn-network"
|
|
||||||
name = "cloud-net-to-on-prem"
|
|
||||||
remote_ranges = ["192.168.192.0/24"]
|
|
||||||
tunnels = {
|
|
||||||
remote-0 = {
|
|
||||||
ike_version = 2
|
|
||||||
peer_ip = module.on-prem.external_address
|
|
||||||
shared_secret = ""
|
|
||||||
traffic_selectors = { local = ["0.0.0.0/0"], remote = null }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module "on-prem" {
|
|
||||||
source = "modules/on-prem-in-a-box/"
|
|
||||||
|
|
||||||
name = "onprem-instance"
|
|
||||||
project_id = "<PROJECT_ID>"
|
|
||||||
zone = "europe-west4-b"
|
|
||||||
network = <NETWORK_NAME>
|
|
||||||
subnet_self_link = "https://www.googleapis.com/compute/v1/projects/<PROJECT_ID>/regions/europe-west4/subnetworks/<SUBNETWORK_NAME>"
|
|
||||||
vpn_gateway_type = "static"
|
|
||||||
peer_ip = module.cloud-vpn.address
|
|
||||||
local_ip_cidr_range = "192.168.192.0/24"
|
|
||||||
shared_secret = module.cloud-vpn.random_secret
|
|
||||||
remote_ip_cidr_ranges = "172.16.0.0/24,172.16.1.0/24,172.16.2.0/24"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Dynamic VPN Gateway
|
|
||||||
```hcl
|
|
||||||
module "cloud-vpn" {
|
|
||||||
source = "modules/net-vpn-dynamic/"
|
|
||||||
project_id = "<PROJECT_ID>"
|
|
||||||
region = "europe-west4"
|
|
||||||
network = "vpn-network"
|
|
||||||
name = "cloud-net-to-on-prem"
|
|
||||||
router_asn = 65001
|
|
||||||
tunnels = {
|
|
||||||
remote-1 = {
|
|
||||||
bgp_peer = {
|
|
||||||
address = "169.254.0.2"
|
|
||||||
asn = 65002
|
|
||||||
}
|
|
||||||
bgp_session_range = "169.254.0.1/30"
|
|
||||||
ike_version = 2
|
|
||||||
peer_ip = module.on-prem.external_address
|
|
||||||
shared_secret = null
|
|
||||||
bgp_peer_options = {
|
|
||||||
advertise_groups = ["ALL_SUBNETS"]
|
|
||||||
advertise_ip_ranges = {
|
|
||||||
}
|
|
||||||
advertise_mode = "DEFAULT"
|
|
||||||
route_priority = 1000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module "on-prem" {
|
|
||||||
source = "modules/on-prem-in-a-box/"
|
|
||||||
|
|
||||||
name = "onprem-instance"
|
|
||||||
project_id = "<PROJECT_ID>"
|
|
||||||
zone = "europe-west4-b"
|
|
||||||
network = "<NETWORK_NAME>"
|
|
||||||
subnet_self_link = "https://www.googleapis.com/compute/v1/projects/<PROJECT_ID>/regions/europe-west4/subnetworks/<SUBNETWORK_NAME>"
|
|
||||||
vpn_gateway_type = "dynamic"
|
|
||||||
peer_ip = module.cloud-vpn.address
|
|
||||||
local_ip_cidr_range = "192.168.192.0/24"
|
|
||||||
shared_secret = module.cloud-vpn.random_secret
|
|
||||||
peer_bgp_session_range = "169.254.0.1/30"
|
|
||||||
local_bgp_session_range = "169.254.0.2/30"
|
|
||||||
peer_bgp_asn = 65001
|
|
||||||
local_bgp_asn = 65002
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<!-- BEGIN TFDOC -->
|
|
||||||
## Variables
|
|
||||||
|
|
||||||
| name | description | type | required | default |
|
|
||||||
|---|---|:---: |:---:|:---:|
|
|
||||||
| network | VPC network name. | <code title="">string</code> | ✓ | |
|
|
||||||
| project_id | Project id. | <code title="">string</code> | ✓ | |
|
|
||||||
| subnet_self_link | VPC subnet self link. | <code title="">string</code> | ✓ | |
|
|
||||||
| vpn_config | VPN configuration, type must be one of 'dynamic' or 'static'. | <code title="object({ peer_ip = string shared_secret = string type = string })">object({...})</code> | ✓ | |
|
|
||||||
| zone | Compute zone. | <code title="">string</code> | ✓ | |
|
|
||||||
| *coredns_config* | CoreDNS configuration, set to null to use default. | <code title="">string</code> | | <code title="">null</code> |
|
|
||||||
| *dns_domain* | DNS domain used for on-prem host records. | <code title="">string</code> | | <code title="">onprem.example.com</code> |
|
|
||||||
| *local_ip_cidr_range* | IP CIDR range used for the Docker onprem network. | <code title="">string</code> | | <code title="">192.168.192.0/24</code> |
|
|
||||||
| *machine_type* | Machine type. | <code title="">string</code> | | <code title="">g1-small</code> |
|
|
||||||
| *name* | On-prem-in-a-box compute instance name. | <code title="">string</code> | | <code title="">onprem</code> |
|
|
||||||
| *network_tags* | Network tags. | <code title="list(string)">list(string)</code> | | <code title="">["ssh"]</code> |
|
|
||||||
| *service_account* | Service account customization. | <code title="object({ email = string scopes = list(string) })">object({...})</code> | | <code title="{ email = null scopes = [ "https://www.googleapis.com/auth/devstorage.read_only", "https://www.googleapis.com/auth/logging.write", "https://www.googleapis.com/auth/monitoring.write" ] }">...</code> |
|
|
||||||
| *vpn_dynamic_config* | BGP configuration for dynamic VPN, ignored if VPN type is 'static'. | <code title="object({ local_bgp_asn = number local_bgp_address = string peer_bgp_asn = number peer_bgp_address = string })">object({...})</code> | | <code title="{ local_bgp_asn = 65002 local_bgp_address = "169.254.0.2" peer_bgp_asn = 65001 peer_bgp_address = "169.254.0.1" }">...</code> |
|
|
||||||
| *vpn_static_ranges* | Remote CIDR ranges for static VPN, ignored if VPN type is 'dynamic'. | <code title="list(string)">list(string)</code> | | <code title="">[]</code> |
|
|
||||||
|
|
||||||
## Outputs
|
|
||||||
|
|
||||||
| name | description | sensitive |
|
|
||||||
|---|---|:---:|
|
|
||||||
| dns_ip_address | None | |
|
|
||||||
| external_address | None | |
|
|
||||||
| instance_name | None | |
|
|
||||||
| internal_address | None | |
|
|
||||||
| toolbox_ip_address | None | |
|
|
||||||
| vpn_ip_address | None | |
|
|
||||||
| web_ip_address | None | |
|
|
||||||
<!-- END TFDOC -->
|
|
|
@ -1,11 +0,0 @@
|
||||||
${dns_domain} {
|
|
||||||
root /etc/coredns
|
|
||||||
hosts onprem.hosts
|
|
||||||
log
|
|
||||||
errors
|
|
||||||
}
|
|
||||||
. {
|
|
||||||
forward . 8.8.8.8
|
|
||||||
log
|
|
||||||
errors
|
|
||||||
}
|
|
|
@ -1,130 +0,0 @@
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
locals {
|
|
||||||
corefile = (
|
|
||||||
var.coredns_config == null || var.coredns_config == ""
|
|
||||||
? data.template_file.corefile.rendered
|
|
||||||
: var.coredns_config
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "google_compute_address" "static" {
|
|
||||||
project = var.project_id
|
|
||||||
name = var.name
|
|
||||||
region = substr(var.zone, 0, length(var.zone) - 2)
|
|
||||||
address_type = "EXTERNAL"
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "google_compute_instance" "on_prem_in_a_box" {
|
|
||||||
project = var.project_id
|
|
||||||
name = var.name
|
|
||||||
machine_type = var.machine_type
|
|
||||||
zone = var.zone
|
|
||||||
tags = concat(var.network_tags, ["onprem"])
|
|
||||||
|
|
||||||
boot_disk {
|
|
||||||
initialize_params {
|
|
||||||
image = "ubuntu-os-cloud/ubuntu-1804-lts"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
network_interface {
|
|
||||||
subnetwork = var.subnet_self_link
|
|
||||||
access_config {
|
|
||||||
nat_ip = google_compute_address.static.address
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
metadata = {
|
|
||||||
user-data = data.template_file.vpn-gw.rendered
|
|
||||||
}
|
|
||||||
|
|
||||||
service_account {
|
|
||||||
email = var.service_account.email
|
|
||||||
scopes = var.service_account.scopes
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
data "template_file" "corefile" {
|
|
||||||
template = file("${path.module}/assets/Corefile")
|
|
||||||
vars = {
|
|
||||||
dns_domain = var.dns_domain
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data "template_file" "vpn-gw" {
|
|
||||||
template = file(format(
|
|
||||||
"%s/assets/%s-vpn-gw-cloud-init.yaml", path.module, var.vpn_config.type
|
|
||||||
))
|
|
||||||
|
|
||||||
vars = {
|
|
||||||
coredns_config = indent(4, local.corefile)
|
|
||||||
dns_domain = var.dns_domain
|
|
||||||
instance_name = var.name
|
|
||||||
local_ip_cidr_range = var.local_ip_cidr_range
|
|
||||||
local_gw_ip = cidrhost(var.local_ip_cidr_range, 1)
|
|
||||||
vpn_ip_address = cidrhost(var.local_ip_cidr_range, 2)
|
|
||||||
dns_ip_address = cidrhost(var.local_ip_cidr_range, 3)
|
|
||||||
web_ip_address = cidrhost(var.local_ip_cidr_range, 4)
|
|
||||||
toolbox_ip_address = cidrhost(var.local_ip_cidr_range, 5)
|
|
||||||
# vpn config
|
|
||||||
peer_ip = var.vpn_config.peer_ip
|
|
||||||
peer_ip_wildcard = "%${var.vpn_config.peer_ip}"
|
|
||||||
shared_secret = var.vpn_config.shared_secret
|
|
||||||
# vpn dynamic config
|
|
||||||
local_bgp_asn = var.vpn_dynamic_config.local_bgp_asn
|
|
||||||
local_bgp_address = var.vpn_dynamic_config.local_bgp_address
|
|
||||||
peer_bgp_asn = var.vpn_dynamic_config.peer_bgp_asn
|
|
||||||
peer_bgp_address = var.vpn_dynamic_config.peer_bgp_address
|
|
||||||
# vpn static ranges
|
|
||||||
vpn_static_ranges = join(",", var.vpn_static_ranges)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# TODO: use a narrower firewall rule and tie it to the service account
|
|
||||||
|
|
||||||
resource "google_compute_firewall" "allow-vpn" {
|
|
||||||
name = "onprem-in-a-box-allow-vpn"
|
|
||||||
description = "Allow VPN traffic to the onprem instance"
|
|
||||||
network = var.network
|
|
||||||
project = var.project_id
|
|
||||||
source_ranges = [format("%s/32", var.vpn_config.peer_ip)]
|
|
||||||
target_tags = ["onprem"]
|
|
||||||
allow {
|
|
||||||
protocol = "tcp"
|
|
||||||
}
|
|
||||||
allow {
|
|
||||||
protocol = "udp"
|
|
||||||
}
|
|
||||||
allow {
|
|
||||||
protocol = "icmp"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "google_compute_firewall" "allow-iap" {
|
|
||||||
name = "onprem-in-a-box-allow-iap"
|
|
||||||
description = "Allow SSH traffic to the onprem instance from IAP"
|
|
||||||
network = var.network
|
|
||||||
project = var.project_id
|
|
||||||
source_ranges = ["35.235.240.0/20"]
|
|
||||||
target_tags = ["onprem"]
|
|
||||||
allow {
|
|
||||||
protocol = "tcp"
|
|
||||||
ports = ["22"]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
output "external_address" {
|
|
||||||
value = google_compute_instance.on_prem_in_a_box.network_interface.0.access_config.0.nat_ip
|
|
||||||
}
|
|
||||||
|
|
||||||
output "internal_address" {
|
|
||||||
value = google_compute_instance.on_prem_in_a_box.network_interface.0.network_ip
|
|
||||||
}
|
|
||||||
|
|
||||||
output "instance_name" {
|
|
||||||
value = google_compute_instance.on_prem_in_a_box.name
|
|
||||||
}
|
|
||||||
|
|
||||||
output "vpn_ip_address" {
|
|
||||||
value = cidrhost(var.local_ip_cidr_range, 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
output "dns_ip_address" {
|
|
||||||
value = cidrhost(var.local_ip_cidr_range, 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
output "web_ip_address" {
|
|
||||||
value = cidrhost(var.local_ip_cidr_range, 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
output "toolbox_ip_address" {
|
|
||||||
value = cidrhost(var.local_ip_cidr_range, 5)
|
|
||||||
}
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module "test" {
|
module "test" {
|
||||||
source = "../../../../modules/cos-container/coredns"
|
source = "../../../../modules/cloud-config-container/coredns"
|
||||||
cloud_config = var.cloud_config
|
cloud_config = var.cloud_config
|
||||||
config_variables = var.config_variables
|
config_variables = var.config_variables
|
||||||
coredns_config = var.coredns_config
|
coredns_config = var.coredns_config
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module "test" {
|
module "test" {
|
||||||
source = "../../../../modules/cos-container/mysql"
|
source = "../../../../modules/cloud-config-container/mysql"
|
||||||
cloud_config = var.cloud_config
|
cloud_config = var.cloud_config
|
||||||
config_variables = var.config_variables
|
config_variables = var.config_variables
|
||||||
image = var.image
|
image = var.image
|
Loading…
Reference in New Issue