#cloud-config # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. package_update: true package_upgrade: true package_reboot_if_required: true packages: - apt-transport-https - ca-certificates - curl - gnupg-agent - software-properties-common write_files: # Docker daemon configuration - path: /etc/docker/daemon.json owner: root:root permissions: '0644' content: | { "log-driver": "json-file", "log-opts": { "max-size": "10m" } } # Docker compose systemd unit for onprem - path: /etc/systemd/system/docker-onprem.service permissions: 0644 owner: root content: | [Install] WantedBy=multi-user.target [Unit] Description=Start Docker Compose onprem infrastructure After=network-online.target docker.socket Wants=network-online.target docker.socket [Service] ExecStart=/bin/sh -c "cd /var/lib/docker-compose/onprem && /usr/local/bin/docker-compose up" ExecStop=/bin/sh -c "cd /var/lib/docker-compose/onprem && /usr/local/bin/docker-compose down" # Docker compose configuration file for onprem - path: /var/lib/docker-compose/onprem/docker-compose.yaml permissions: 0644 owner: root content: | version: "3" services: vpn: image: gcr.io/pso-cft-fabric/strongswan:latest networks: onprem: ipv4_address: ${local_addresses.vpn} ports: - "500:500/udp" - "4500:4500/udp" %{~ if vpn_config.type == "dynamic" ~} - "179:179/tcp" %{~ endif ~} privileged: true cap_add: - NET_ADMIN volumes: - "/lib/modules:/lib/modules: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.secrets:/etc/ipsec.secrets:ro" %{~ if vpn_config.type == "dynamic" ~} - "/var/lib/docker-compose/onprem/ipsec/vti.conf:/etc/strongswan.d/vti.conf:ro" %{~ endif ~} environment: - LAN_NETWORKS=${ip_cidr_ranges.local} %{~ if vpn_config.type == "dynamic" ~} bird: image: pierky/bird network_mode: service:vpn cap_add: - NET_ADMIN - NET_BROADCAST - NET_RAW privileged: true volumes: - "/var/lib/docker-compose/onprem/bird/bird.conf:/etc/bird/bird.conf:ro" %{~ endif ~} dns: image: coredns/coredns command: "-conf /etc/coredns/Corefile" depends_on: - "vpn" %{~ if vpn_config.type == "dynamic" ~} - "bird" %{~ endif ~} networks: onprem: ipv4_address: ${local_addresses.dns} volumes: - "/var/lib/docker-compose/onprem/coredns:/etc/coredns:ro" routing_sidecar_dns: image: alpine network_mode: service:dns command: | /bin/sh -c "\ ip route del default &&\ ip route add default via ${local_addresses.vpn}" privileged: true web: image: nginx:stable-alpine depends_on: - "vpn" %{~ if vpn_config.type == "dynamic" ~} - "bird" %{~ endif ~} - "dns" dns: - ${local_addresses.dns} networks: onprem: ipv4_address: ${local_addresses.www} volumes: - "/var/lib/docker-compose/onprem/nginx:/usr/share/nginx/html:ro" routing_sidecar_web: image: alpine network_mode: service:web command: | /bin/sh -c "\ ip route del default &&\ ip route add default via ${local_addresses.vpn}" privileged: true toolbox: image: gcr.io/pso-cft-fabric/toolbox:latest networks: onprem: ipv4_address: ${local_addresses.shell} depends_on: - "vpn" - "dns" - "web" dns: - ${local_addresses.dns} routing_sidecar_toolbox: image: alpine network_mode: service:toolbox command: | /bin/sh -c "\ ip route del default &&\ ip route add default via ${local_addresses.vpn}" privileged: true networks: onprem: ipam: driver: default config: - subnet: ${ip_cidr_ranges.local} # IPSEC tunnel secret - path: /var/lib/docker-compose/onprem/ipsec/ipsec.secrets owner: root:root permissions: '0600' content: | ${vpn_config.peer_ip} : PSK "${vpn_config.shared_secret}" ${vpn_config.peer_ip2} : PSK "${vpn_config.shared_secret2}" # IPSEC tunnel configuration - path: /var/lib/docker-compose/onprem/ipsec/ipsec.conf owner: root:root permissions: '0644' content: | conn %default ikelifetime=600m keylife=180m rekeymargin=3m keyingtries=3 keyexchange=ikev2 mobike=no ike=aes256gcm16-sha512-modp2048 esp=aes256gcm16-sha512-modp8192 authby=psk conn gcp %{~ 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 leftid=%any %{~ if vpn_config.type == "dynamic" ~} leftsubnet=0.0.0.0/0 %{~ else ~} leftsubnet=${ip_cidr_ranges.local} %{~ endif ~} leftauth=psk right=${vpn_config.peer_ip_wildcard} rightid=${vpn_config.peer_ip} %{~ if vpn_config.type == "dynamic" ~} rightsubnet=0.0.0.0/0 %{~ else ~} rightsubnet=${ip_cidr_ranges.remote} %{~ endif ~} rightauth=psk type=tunnel auto=start dpdaction=restart closeaction=restart %{~ if vpn_config.type == "dynamic" ~} mark=%unique %{~ endif ~} conn gcp2 %{~ if vpn_config.type == "dynamic" ~} leftupdown="/var/lib/strongswan/ipsec-vti.sh 1 ${vpn_dynamic_config.peer_bgp_address2}/30 ${vpn_dynamic_config.local_bgp_address2}/30" %{~ endif ~} left=%any leftid=%any %{~ if vpn_config.type == "dynamic" ~} leftsubnet=0.0.0.0/0 %{~ else ~} leftsubnet=${ip_cidr_ranges.local} %{~ endif ~} leftauth=psk right=${vpn_config.peer_ip_wildcard2} rightid=${vpn_config.peer_ip2} %{~ if vpn_config.type == "dynamic" ~} rightsubnet=0.0.0.0/0 %{~ else ~} rightsubnet=${ip_cidr_ranges.remote} %{~ endif ~} rightauth=psk type=tunnel auto=start dpdaction=restart closeaction=restart %{~ if vpn_config.type == "dynamic" ~} mark=%unique %{~ endif ~} %{~ if vpn_config.type == "dynamic" ~} # Charon configuration - path: /var/lib/docker-compose/onprem/ipsec/vti.conf owner: root:root permissions: '0644' content: | charon { install_routes = no } # Bird bgp routing configuration - path: /var/lib/docker-compose/onprem/bird/bird.conf owner: root:root permissions: '0644' content: | router id ${vpn_dynamic_config.local_bgp_address}; # watch interface up/down events protocol device { scan time 10; } # sync routes to kernel protocol kernel { learn; merge paths on; # For ECMP export filter { # internal IP of the strongswan VM krt_prefsrc = ${local_addresses.vpn}; # sync all routes to kernel accept; }; import all; # Required due to /32 on GCE VMs for the static route below } # Configure a static route to make sure route exists protocol static { # network connected to eth0 route ${ip_cidr_ranges.local} recursive ${local_addresses.gw}; %{~ for range in netblocks ~} # route ${range} via ${vpn_dynamic_config.peer_bgp_address}; %{~ endfor ~} } # prefix lists for routing security # allow any possible GCP Subnet 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 GCP_NETBLOCKS = [ ${join(", ", netblocks)} ]; define LOCAL_PREFIXES = [ ${ip_cidr_ranges.local} ]; # filter received prefixes filter gcp_vpc_a_in { if (net ~ GCP_VPC_A_PREFIXES || net ~ GCP_NETBLOCKS) then accept; else reject; } # filter advertised prefixes filter gcp_vpc_a_out { if (net ~ LOCAL_PREFIXES) then accept; else reject; } template bgp gcp_vpc_a { keepalive time 20; hold time 60; # Cloud Router uses GR during maintenance graceful restart aware; import filter gcp_vpc_a_in; import limit 10 action warn; # restart | block | disable export filter gcp_vpc_a_out; export limit 10 action warn; # restart | block | disable } protocol bgp gcp_vpc_a_tun1 from gcp_vpc_a { local ${vpn_dynamic_config.local_bgp_address} as ${vpn_dynamic_config.local_bgp_asn}; neighbor ${vpn_dynamic_config.peer_bgp_address} as ${vpn_dynamic_config.peer_bgp_asn}; } protocol bgp gcp_vpc_a_tun2 from gcp_vpc_a { local ${vpn_dynamic_config.local_bgp_address2} as ${vpn_dynamic_config.local_bgp_asn2}; neighbor ${vpn_dynamic_config.peer_bgp_address2} as ${vpn_dynamic_config.peer_bgp_asn2}; } %{~ endif ~} # CoreDNS configuration - path: /var/lib/docker-compose/onprem/coredns/Corefile owner: root:root permissions: '0644' content: | ${coredns_config} # CoreDNS onprem hosts file - path: /var/lib/docker-compose/onprem/coredns/onprem.hosts owner: root:root permissions: '0644' content: | %{~ for name, address in local_addresses ~} ${address} ${name}.onprem.example.org %{~ endfor ~} # Minimal nginx index page - path: /var/lib/docker-compose/onprem/nginx/index.html owner: root:root permissions: '0644' content: |

On Prem in a Box

onprem

runcmd: - [systemctl, daemon-reload] - [ sh, -c, 'curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -' ] - [ sh, -c, 'add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"' ] - [ sh, -c, 'apt update' ] - [ sh, -c, 'apt install -y docker-ce docker-ce-cli containerd.io' ] - [ sh, -c, 'curl -L https://github.com/docker/compose/releases/download/$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep "tag_name" | cut -d \" -f4)/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose' ] - [ sh, -c, 'chmod 755 /usr/local/bin/docker-compose' ] - [systemctl, enable, docker.service] - [systemctl, start, docker.service] - [systemctl, enable, docker-onprem.service] - [systemctl, start, docker-onprem.service]