GCP Bootstrap

furyctl gcp bootstrap provisioner

8 minute read

GCP Bootstrap

This provisioner deploys a battle-tested network configuration with VPN instances. The provisioner enables the deployment of all these components with a simple yaml file. It provides some excellent features:

  • A Network fully configured to host a Fury GKE Cluster.
    • Enables the deployment of Internal and External Load Balancers from Kubernetes.
    • Enables the auto-scaling feature for Kubernetes workloads.
  • Open/Close ssh access to specific IP Ranges (CIDR)
  • Open/Close ssh access to particular users (public keys gathered from GitHub)
  • Scale the VPN instance (horizontal and vertical)
  • many more…

Configuration

kind: Bootstrap
metadata:
  name: # The name of the resources. Used to name the network, upcoming cluster name, VPN...
provisioner: gcp # set to `gcp` to use this provisioner
spec:
  publicSubnetsCIDRs: # Public subnets to create. VPN instances will be deployed in these subnets.
    # - "10.0.1.0/24" # Example Value
    # - "10.0.2.0/24" # Example Value
    # - "10.0.3.0/24" # Example Value
  privateSubnetsCIDRs: # Private subnets to create. Place here any surrounded cluster components like databases.
    # - "10.0.101.0/24" # Example Value
    # - "10.0.102.0/24" # Example Value
    # - "10.0.103.0/24" # Example Value
  clusterNetwork: # This attributes define some networking configuration to host a GKE cluster. Required.
    subnetworkCIDR: '10.1.0.0/16' # Required. Cluster nodes subnetwork
    controlPlaneCIDR: '10.0.0.0/28' # Optional. Control Plane CIDR. This value is the default one.
    podSubnetworkCIDR: '10.2.0.0/16' # Required. Pod subnetwork CIDR
    serviceSubnetworkCIDR: '10.3.0.0/16' # Required. Service subnetwork CIDR
  vpn: # VPN Configuration
    instances: # Optional vpn instances/replicas. Configures the number of VPN instances. Default value 1. Valid values >= 0
    instanceType: # Optional vpn attribute. Configures the instance size. Default value n1-standard-1.
    port: # Optional vpn attribute. Configures the port that exposes the vpn service. Default value 1194.
    diskSize: # Optional vpn attribute. Configures the root disk size of the vpn instance. Default value 50.
    dhParamsBits: # Optional vpn attribute. Configures the size of the diffie hellman exchange key. Default value 2048
    subnetCIDR: # Configures the vpn subnet. Example value: 192.168.200.0/24
    sshUsers: # GitHub identities that can log into the instance using ssh.
    #   - angelbarrera92 # Example value
    operatorName: # SSH user name to log into the instance. Use a private key of the sshUsers identities. Default value: sighup
    operatorCIDRs: # CIDR Range to access the instance via SSH.
    #   - 0.0.0.0/0 # Default value

Important notes

  • Network related configuration can not be easily modifiable. All the network configuration is required. Design your networking configuration properly before execute furyctl bootstrap apply.
  • Some vpn attributes are required; others are optional.
    • Changing any of instanceType, operatorCIDRs or sshUsers can be done without downtime. It can take up to five minutes to apply the configuration.
    • Changing any of port, diskSize, dhParamBits, subnetCIDR or operatorName can be done anytime but it will cause downtime and may require reconfiguration of openvpn client configuration files.
    • instances can be scale to 0 if needed. It will cause downtime. Recommended to spin up at least two instances.

Resources

This provisioner creates and configures:

  • Network: A new virtual network with the network configuration configured in the yaml file.
  • Cloud Nat: Enable instances in private subnets to connect to the internet or other GCP services, but prevent the internet from initiating a connection with those instances.
  • Subnets: Configured along with route tables, it creates public-facing and private subnets.
  • GCP Instance: VPN instances with ubuntu 20.04 as the operating system. It has installed and configured OpenVPN, furyagent and the ssh users synchronizer. It has a dedicated elastic IP attached to the instance so that the IP will remain the same in case of recreation.
  • GCS Bucket: A bucket with the ssh and VPN configuration. It is required to recover the VPN instance in case of a disaster. A service account with permissions to read the bucket is created to accomplish these backups/recovery stuff.

Diagram

Requirements

This provisioner requires to have previously configured:

  • GCP Credentials in form of environment variables:
    • One of: GOOGLE_CREDENTIALS | GOOGLE_CLOUD_KEYFILE_JSON | GCLOUD_KEYFILE_JSON
    • One of: GOOGLE_PROJECT | GOOGLE_CLOUD_PROJECT | GCLOUD_PROJECT | CLOUDSDK_CORE_PROJECT
    • One of: GOOGLE_REGION | GCLOUD_REGION | CLOUDSDK_COMPUTE_REGION
    • Enough permissions to deploy the above resources.
  • furyctl.
  • furyagent.
  • A configuration file with all the values in place. Please think about it very carefully, some values are inmmutable.

Example execution

In the following lines, you will find an example execution to deploy the bootstrap infrastructure.

First, ensure you pass the requirements. Then create a configuration file with the structure described above.

First, create a new directory:

$ mkdir demo
$ cd demo

Take this bootstrap.yml file as an example. We recommend you to use a non-default backend configuration:

kind: Bootstrap
metadata:
  name: furyctl
provisioner: gcp
spec:
  publicSubnetsCIDRs:
    - 10.0.1.0/24
  privateSubnetsCIDRs:
    - 10.0.101.0/24
  clusterNetwork:
    subnetworkCIDR: 10.1.0.0/16
    podSubnetworkCIDR: 10.2.0.0/16
    serviceSubnetworkCIDR: 10.3.0.0/16
  vpn:
    subnetCIDR: 192.168.200.0/24
    sshUsers:
      - angelbarrera92
$ ls
bootstrap.yml

Init the bootstrap project

$ furyctl bootstrap init --reset --token $FURYCTL_TOKEN
WARN[0000] Cleaning up the workdir
WARN[0000] Removing ./bootstrap directory
WARN[0000] could not find terraform executable
INFO[0007] [INFO] running Terraform command: bootstrap/bin/terraform init -no-color -force-copy -input=false -lock-timeout=0s -backend=true -get=true -get-plugins=true -lock=true -upgrade=false -verify-plugins=true
[GCP] - VPC and VPN

This provisioner creates a battle-tested GCP VPC with all the requirements
set to run a production-grade private GKE cluster.

It creates VPN servers enables deploying the cluster from this computer
once connected to the VPN server.

Then, use furyagent to manage VPN profiles.

[FURYCTL]

Init phase completed.

Project directory: ./bootstrap
Terraform logs: ./bootstrap/logs/terraform.logs

Everything ready to create the infrastructure; execute:

$ furyctl bootstrap apply

Once completed, a new directory bootstrap is available inside the current directory demo.

$ ls
bootstrap   bootstrap.yml

It contains all the terraform project and configuration to properly manage the infrastrucutre.

$ tree bootstrap
bootstrap
├── backend.tf
├── bin
│   └── terraform
├── configuration
├── gcp.tfvars
├── logs
│   └── terraform.logs
├── main.tf
├── output
├── output.tf
├── secrets
├── ssh-users.yml
└── variables.tf

5 directories, 8 files

It didn’t create any infrastructure component; continue the example to deploy it.

Deploy the bootstrap project

$ furyctl bootstrap apply --token $FURYCTL_TOKEN
ERRO[0000] Directory already exists
WARN[0000] error while updating project subdirectories: Directory already exists
INFO[0000] terraform is up to date
INFO[0000] [INFO] running Terraform command: bootstrap/bin/terraform init -no-color -force-copy -input=false -lock-timeout=0s -backend=true -get=true -get-plugins=true -lock=true -upgrade=false -verify-plugins=true
⡿ Re-Initializing terraform project INFO[0002] Updating GCP Bootstrap project
INFO[0002] [INFO] running Terraform command: bootstrap/bin/terraform fmt -no-color -write=true -list=false -diff=false ./bootstrap/gcp.tfvars
⣟ Applying terraform project INFO[0002] [INFO] running Terraform command: bootstrap/bin/terraform apply -no-color -auto-approve -input=false -var-file=./bootstrap/gcp.tfvars -lock=true -parallelism=10 -refresh=true
⢿ Applying terraform project
⣟ Applying terraform project INFO[0109] GCP Updated
INFO[0109] Gathering output file as json
INFO[0109] [INFO] running Terraform command: bootstrap/bin/terraform output -no-color -json
INFO[0120] [INFO] running Terraform command: bootstrap/bin/terraform output -no-color -json
[GCP] - VPC and VPN

All the bootstrap components are up to date.

VPC and VPN ready:

VPC: furyctl
Public Subnets  : [furyctl-public-subnet-1]
Private Subnets : [furyctl-private-subnet-1]
Cluster Subnet  : furyctl-cluster-subnet
  Pod Subnet    : furyctl-cluster-pod-subnet
  Service Subnet: furyctl-cluster-service-subnet

Your VPN instance IPs are: [35.187.30.231]
Use the ssh sighup username to access the VPN instance with any SSH key configured
for the following GitHub users: [angelbarrera92].

$ ssh sighup@35.187.30.231

Then create a openvpn configuration (ovpn) file using the furyagent cli:

$ furyagent configure openvpn-client --client-name <your-name-goes-here> --config ./bootstrap/secrets/furyagent.yml > <your-name-goes-here>.ovpn

Discover already registered vpn clients running:

$ furyagent configure openvpn-client --list --config ./bootstrap/secrets/furyagent.yml

IMPORTANT! Connect to the VPN with the created ovpn profile to continue deploying
an GKE Kubernetes cluster.

[FURYCTL]
Apply phase completed.

Project directory: ./bootstrap
Terraform logs: ./bootstrap/logs/terraform.logs
Output file: ./bootstrap/output/output.json

Everything is up to date.
Ready to apply or destroy the infrastructure; execute:

$ furyctl bootstrap apply
or
$ furyctl bootstrap destroy

Once completed, everything is ready to start using the VPN instance along with other bootstrap components. In the output message there is enough information to start using the new infrastructure:

VPC: furyctl
Public Subnets  : [furyctl-public-subnet-1]
Private Subnets : [furyctl-private-subnet-1]
Cluster Subnet  : furyctl-cluster-subnet
  Pod Subnet    : furyctl-cluster-pod-subnet
  Service Subnet: furyctl-cluster-service-subnet
Your VPN instance IPs are: [35.187.30.231]
Use the ssh sighup username to access the VPN instance with any SSH key configured
for the following GitHub users: [angelbarrera92].
$ ssh sighup@35.187.30.231
$ furyagent configure openvpn-client --client-name <your-name-goes-here> --config ./bootstrap/secrets/furyagent.yml
$ furyagent configure openvpn-client --list --config ./bootstrap/secrets/furyagent.yml

Create an ovpn profile by executing the command above (replace <your-name-goes-here> with your username):

$ furyagent configure openvpn-client --client-name angelbarrera92 --config ./bootstrap/secrets/furyagent.yml > angelbarrera92.ovpn
2020-12-18 13:34:58.303002 I | openvpnclient.go:190: Downloading ca.crt, ca.key and ta.key
2020-12-18 13:34:58.341379 I | storage.go:146: Item pki/vpn/ca.crt found [size: 1082]
2020-12-18 13:34:58.341397 I | storage.go:147: Saving item pki/vpn/ca.crt ...
2020-12-18 13:34:58.432603 I | storage.go:146: Item pki/vpn/ca.key found [size: 1675]
2020-12-18 13:34:58.432620 I | storage.go:147: Saving item pki/vpn/ca.key ...
2020-12-18 13:34:58.509569 I | storage.go:146: Item pki/vpn/ta.key found [size: 636]
2020-12-18 13:34:58.509587 I | storage.go:147: Saving item pki/vpn/ta.key ...
2020-12-18 13:34:58.575061 I | openvpnclient.go:195: Creating client cert for:  angelbarrera92
2020-12-18 13:34:58.729849 I | openvpnclient.go:235: Uploading client cert for:  angelbarrera92
$ ls
bootstrap   bootstrap.yml   angelbarrera92.ovpn

Open the profile with your preferred ovpn client, then connect to the VPN.

Modify the configuration

For example, modification of the stack, to allow other ssh users to access the instance (as an example), modify the bootstrap.yml file adding the right GitHub user id (to apply the new public keys in the instance).

kind: Bootstrap
metadata:
  name: furyctl
provisioner: gcp
spec:
  publicSubnetsCIDRs:
    - 10.0.1.0/24
  privateSubnetsCIDRs:
    - 10.0.101.0/24
  clusterNetwork:
    subnetworkCIDR: 10.1.0.0/16
    podSubnetworkCIDR: 10.2.0.0/16
    serviceSubnetworkCIDR: 10.3.0.0/16
  vpn:
    subnetCIDR: 192.168.200.0/24
    sshUsers:
      - angelbarrera92
      - ralgozino # New ssh operator

Then run again the furyctl bootstrap apply command.

$ furyctl bootstrap apply --token $FURYCTL_TOKEN

The new user can take up to five minutes to be able to access the VPN instance.

Destroy the bootstrap project

If you need to destroy the bootstrap project, first ensure nothing is blocking the destruction of this infrastructure such as GKE clusters or any other component different from the members deployed with this provisioner.

Ensure to disconnect the VPN connection before proceed:

Then run:

$ furyctl bootstrap destroy
  Are you sure you want to destroy it?
  Write 'yes' to continue
yes
ERRO[0001] Directory already exists
WARN[0001] error while updating project subdirectories: Directory already exists
INFO[0001] terraform is up to date
INFO[0001] [INFO] running Terraform command: bootstrap/bin/terraform init -no-color -force-copy -input=false -lock-timeout=0s -backend=true -get=true -get-plugins=true -lock=true -upgrade=false -verify-plugins=true
INFO[0003] Destroying GCP Bootstrap project
INFO[0003] [INFO] running Terraform command: bootstrap/bin/terraform fmt -no-color -write=true -list=false -diff=false ./bootstrap/gcp.tfvars
⣯ Destroying terraform project INFO[0003] [INFO] running Terraform command: bootstrap/bin/terraform destroy -no-color -auto-approve -input=false -lock-timeout=0s -var-file=./bootstrap/gcp.tfvars -lock=true -parallelism=10 -refresh=true
⣷ Destroying terraform project INFO[0092] GCP Bootstrap destroyed
[GCP] - VPC and VPN
All bootstrap components were destroyed.
VPN and VPC went away.

Had problems, contact us at sales@sighup.io.

[FURYCTL]
Destroy phase completed.

Project directory: ./bootstrap
Terraform logs: ./bootstrap/logs/terraform.logs