…Or someone else’s computer

Sometimes your development machine is not sufficient for various reasons: For example, you need to build an x86/amd64 Docker image and do not want to pay the cost of QEMU, or it is on a limited internet; for these and other reasons, I created a terraform to bootstrap a development machine with a single command.

Of course, Codespaces is the ideal solution for this. However, it does not exist in my region, and it is expensive.

My solution relies on the following:

  • Terraform for provisioning and de-provisioning.
  • Cloud-init for initial configuration.
  • Tailscale to create a secure tunnel between the machine located in the cloud and my machine.
  • asdf for installing languages and tools.

I have chosen Vultr because they are cheap and have machines in my region.

Provisioning

Let us take a look into the terraform script, which is responsible for provisioning the machine and setting the Cloud-init:

locals {
  user_data = templatefile("cloud-init.yaml", {
    user  = var.USER
    tskey = var.TAILSCALE_AUTHKEY
  })
}

resource "vultr_instance" "workstation" {
  plan        = "vc2-1c-1gb-sc1"
  region      = "sao"
  os_id       = "387"
  enable_ipv6 = true

  user_data = local.user_data
}

variable "USER" {}
variable "TAILSCALE_AUTHKEY" {}

Another important script is the cloud-config. It is used by Cloud-init to run a set of commands at the very first boot. We will install Docker and its plugins, Tailscale, and asdf.

Please note that we are not messing with ports; we will keep all closed, and the only access is through the VPN (Tailscale).

#cloud-config

---
users:
  - name: ${user}
    shell: /usr/bin/bash
    ssh_import_id: gh:${user}
    sudo: ALL=(ALL:ALL) NOPASSWD:ALL
    groups: docker
    lock_passwd: true

chpasswd:
  expire: false

ssh_pwauth: false

apt_update: true

apt_upgrade: true

apt:
  sources:
    tailscale.list:
      source: deb https://pkgs.tailscale.com/stable/ubuntu focal main
      keyid: 2596A99EAAB33821893C0A79458CA832957F5868
    docker.list:
      source: deb https://download.docker.com/linux/ubuntu focal stable
      keyid: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88

packages: 
  - curl
  - git
  - docker-ce
  - docker-ce-cli
  - docker-compose-plugin
  - tailscale

runcmd:
  - |
    cat << 'EOF' >> /home/${user}/.bashrc
    . $HOME/.asdf/asdf.sh
    . $HOME/.asdf/completions/asdf.bash
    EOF    
  - su ${user} -c 'git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.11.0'
  - tailscale up -authkey ${tskey}

Another interesting point, we are going to fetch the public keys for SSH access from GitHub, that is what this line does:

ssh_import_id: gh:${user}

With all variables in place and the region and plan selected, we can start the provisioning.

Copy the terraform.tfvars.example to terraform.tfvars and fill out the values:

VULTR_APIKEY = "apikey"
TAILSCALE_AUTHKEY = "authkey"
USER = "github_username"

Then, use terraform to apply:

terraform init
terraform apply

During the setup, it gets configured the Tailscale and the SSH public keys fetched from GitHub, this allows connecting to the machine with a single command:

It can take a while, you can follow the progress on the dashboard or wait for the new machine has been added to Tailscale.

ssh vultr

This is possible because Tailscale has a feature named magic DNS, which will add the hostname of each machine, by this way, you do not need to remember addresses or open ports, everything works out of the box.

It is also possible to use VSCode remote development, which turns it a real game changer.

Exposing to the Internet

Remember I told you you don’t need to open ports or IP addresses?

With Cloudflare Tunnel, you can remain occult, and the only traffic is coming up from Cloudflare with Tunnel. A true zero-trust environment.

Let’s suppose you have your blog running and binding only on localhost. You can expose it by running the command below; you will need an account and a domain set on Cloudflare.

cloudflared login
cloudflared tunnel run --url localhost:3000 blog

You can also run it as a service. To do this, run:

cloudflared service install

More details on how to setup Cloudflare Tunnel in https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-guide/local/.

WARP

If for any reason you need an alternative to Tailscale, Cloudflare WARP is a good option. Cloudflare WARP is technically a VPN; it connects to Cloudflare’s 1.1.1.1. Cloudflare has a lot of PoPs (Point of Presence) around the world, which could have a better decision on the routes and delivery a lower latency.

The setup is pretty much the same as used to expose an internal service to the internet by using cloudflared.

tunnel: 953db4ee-d6c7-4e63-b5a0-7b02f4258abd
credentials-file: /root/.cloudflared/953db4ee-d6c7-4e63-b5a0-7b02f4258abd.json
warp-routing:
enabled: true

Then run:

cloudflared tunnel run yourservice

With WARP, you can create fine-grained rules of who can access the service; for example, all people with email @company.com.

More information on this page.

Costs

You only pay for the time while the cloud instance is running, to dispose of it, you just need to run:

terraform destroy

Source Code

github.com/skhaz/my-cloud-workspace

Related articles: