This module manages a Google Cloud bastion compute instance and its regional Auto Scaling Group, service account, SSH firewall rule, and SSH access. The Auto Scaling Group will recreate the bastion if there is an issue with the compute instance or the availability zone where it is running.
The startup script assumes the Ubuntu operating system, which is configured as follows:
- Packages are updated, and the bastion is rebooted if required.
- If SSH hostkeys are present in the configurable GCS bucket and path, they are copied to the bastion to retain its previous SSH identity. If there are no host keys in GCS, the current keys are copied there.
- The Google Accounts daemon is disabled so that SSH access is managed exclusively by this module. This disables the ability to use
gcloud compute ssh ...to SSH to the bastion. - The Stackdriver Logging agent is installed and configured to ship logs from these files:
/var/log/syslog/var/log/auth.log
- A host record, named using the
bastion_namemodule input, is added to a configurable Google DNS managed DNS zone for the current public IP address of the bastion. This happens via a script configured to run each time the bastion boots. - Automatic updates are configured, using a configurable time to reboot, and the email address to receive errors.
- By default sudo access is removed from the ubuntu user unless the
remove_root_accessinput is set to "false." - Additional startup script commands can be executed, for one-off configuration not included in this module.
- Additional users can be created and populated with their own
authorized_keysfile.
To proxy SSH connections to Kubernetes nodes through the bastion, add configuration like the following to the top of the ssh_config file. Replace the following information with your own values:
domain.comwith the same domain name that matches the Google DNS zone name in the instance of the bastion Terraform module. This is the domain name where the bastions host record will have been created during boot./path/to/ssh/private/keywith the path to your SSH private key file.172.20.*.*with the network CIDR used by Kubernetes node compute instances.
# Define options to be used when connecting to the bastion.
host bastion.domain.com
IdentityFile /path/to/ssh/private/key
IdentitiesOnly yes
User ubuntu
# Use the bastion to proxy SSH connections to IPs in the Kubernetes node network
# You can also add a DNS wildcard to the end of the next line
# if you use DNS resolution to access Kubernetes nodes.
host 172.20.*.*
ProxyCommand ssh -i /path/to/ssh/private/key ubuntu@bastion.domain.com -W %h:%p
You can now use gcloud compute ssh --internal-ip your_gke_node_hostname to SSH directly to IP addresses within 172.20.0.0/16, and your connection will be proxied through the bastion.
You can proxy access to a private Kubernetes API through the bastion, instead of using a VPN.
Run the following to forward connections from port 8443 on your workstation, to a private Kubernetes API, - replace api.clustername.domain.com with your private API hostname, and bastion.domain.com with the hostname of your bastion:
ssh -L 8443:api.clustername.domain.com:443 ubuntu@bastion.domain.com
In another terminal tab, edit your KubeConfig and replace api.clustername.domain.com with 127.0.0.1:8443 in the server line for your private cluster.
With the above, as long as the SSH proxy connection remains active, you can use kubectl to access your private Kubernetes cluster. Close the SSH connection in the other terminal tab to stop proxying to the private API.
The sshuttle tool uses NAT redirect firewall rules to proxy access to a network over a bastion. This is useful to connect to multiple ports on multiple hosts without maintaining a lot of SSH forwarding.
The bastion already has Python installed, which sshuttle requires to be on the bastion. Once you have installed sshuttle on your workstation, use the following to redirect access to your network CIDR over the bastion - replacing bastion.domain.com with your bastion hostname, and 172.20.0.0/16 with your network CIDR:
sshuttle -r ubuntu@bastion.domain.com 172.20.0.0/16
You can now access your private Kubernetes API using the internal API hostname in the KubeConfig, and SSH directly to Kubernetes nodes without any proxy configuration defined in your ssh_config file.
Press CTRL-c to kill sshuttle when you are done with the proxy. There are many useful sshuttle command-line options, such as running in the background, and specifying the CIDR to redirect in a file.
See the file example-usage for an example of how to use this module. Below are the available module inputs:
The following requirements are needed by this module:
-
terraform (>= 0.13)
-
google (>=4.27.0)
The following providers are used by this module:
-
google (>=4.27.0)
-
template
The following input variables are required:
Description: The availability zones within $region where the Auto Scaling Group can place the bastion.
Type: list
Description: The name of the Google DNS zone for the bastion to add its host record. Specify the name of the managed zone, not the domain name.
Type: any
Description: An GCS bucket to store data that should persist on the bastion when it is recycled by the Auto Scaling Group, such as SSH host keys. This can be set in the environment via TF_VAR_infrastructure_bucket
Type: any
Description: The name of the network where the bastion SSH firewall rule will be created. This network is the parent of $subnetwork
Type: any
Description: The region where the bastion should be provisioned. This is a required input for the google_compute_region_instance_group_manager Terraform resource, and is not inherited from the provider.
Type: any
Description: The content of an existing SSH public key file, that will be used with the ssh-keys GCP metadata to allow SSH access. Yes, this input has an unfortunate name.
Type: any
Description: The name of the existing subnetwork where the bastion will be created.
Type: any
Description: An email address where unattended upgrade errors should be emailed. THis sets the option in /etc/apt/apt.conf.d/50unattended-upgrades
Type: any
The following input variables are optional (have default values):
Description: Additional users to be created on the bastion. Works the same as additional_users, but adds users via a separate systemd unit file. Specify users as a list of maps. See an example in the example-usage file. Required map keys are login (user name) and authorized_keys. Optional map keys are gecos (full name), supplemental_groups (comma-separated), and shell. The authorized_keys will be output to ~/.ssh/authorized_keys using printf - multiple keys can be specified by including \n in the string.
Type: list
Default: []
Description: Content to be appended to the setup script, which is run the first time the bastion compute instance boots.
Type: string
Default: ""
Description: Additional users to be created on the bastion. Specify users as a list of maps. See an example in the example-usage file. Required map keys are login (user name) and authorized_keys. Optional map keys are gecos (full name), supplemental_groups (comma-separated), and shell. The authorized_keys will be output to ~/.ssh/authorized_keys using printf - multiple keys can be specified by including \n in the string.
Type: list
Default: []
Description: The name of the bastion compute instance, DNS hostname, IAM service account, and the prefix for resources such as the firewall rule, instance template, and instance group.
Type: string
Default: "ro-bastion"
Description: The family for the compute image. This module has assumptions about the OS being Ubuntu.
Type: string
Default: "ubuntu-1804-lts"
Description: The project of the compute image owner.
Type: string
Default: "ubuntu-os-cloud"
Description: The key; sub-directory in $infrastructure_bucket where the bastion will be allowed to read and write. Do not specify a trailing slash. This allows sharing a GCS bucket among multiple invocations of this module.
Type: string
Default: "bastion"
Description: The GCE machine type of the bastion.
Type: string
Default: "n1-standard-1"
Description: Enables confidential compute for the bastions vm instance.
Type: bool
Default: "false"
Description: Enables secure boot for the bastions vm instance.
Type: bool
Default: "false"
Description: How to handle host maintenance events for this VM. Must be one of MIGRATE or TERMINATE.
Type: string
Default: "MIGRATE"
Description: Whether to remove root access from the ubuntu user. Set this to yes|true|1 to remove root access, or anything else to retain it.
Type: string
Default: "true"
Description: A list of CIDRs allowed to SSH to the bastion. Override the module default by specifying an empty list, []
Type: list(string)
Default:
[
"0.0.0.0/0"
]Description: Additional configuration lines to add to /etc/apt/apt.conf.d/50unattended-upgrades
Type: string
Default: ""
Description: The time that the bastion should reboot, when necessary, after an an unattended upgrade. This sets the option in /etc/apt/apt.conf.d/50unattended-upgrades
Type: string
Default: "21:30"
No output.
We are happy to share this internal module with the community. We appreciate suggestions for improvement, and recommend starting by opening an issue. Please see contributing.md for details.
The design document describes the goals and vision for this project.