Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Hybrid Cloud — Multi-Cloud WireGuard VPN

Provision a WireGuard VPN server on any supported cloud with Terraform, configure it with Ansible, and manage DNS through Cloudflare — all from a single Docker-based toolchain. No local Terraform/Ansible install required (works on Windows too).

Why this layout

Multi-cloud Terraform has no native polymorphism, so each provider is hidden behind a module that honors one common contract, and the rest of the system only ever sees a normalized server_ipv4. Three concerns are deliberately decoupled:

Layer Tool Notes
Provision server + firewall Terraform, one module per cloud the only cloud-specific part
Configure WireGuard Ansible (roles/wireguard) cloud-agnostic; just needs SSH to a Debian box
DNS Cloudflare provider-agnostic, so switching clouds never breaks DNS

This replaces the old user_data: curl | bash bootstrap: provisioning and configuration are now separate, which is what lets the same WireGuard setup run on every provider.

Supported providers

Cloud Status Stack Credentials (.env)
DigitalOcean terraform/stacks/digitalocean DIGITALOCEAN_TOKEN
Hetzner terraform/stacks/hetzner HCLOUD_TOKEN
AWS terraform/stacks/aws AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
GCP terraform/stacks/gcp GOOGLE_CREDENTIALS, GOOGLE_PROJECT
Azure terraform/stacks/azure ARM_CLIENT_ID/SECRET/TENANT_ID/SUBSCRIPTION_ID
Linode terraform/stacks/linode LINODE_TOKEN

DNS (any cloud) also needs CLOUDFLARE_API_TOKEN. All stacks are complete and pass terraform validate against the real provider schemas. As with any IaC, run a plan with real credentials as a final smoke test — see each provider's guide in docs/providers/ for region/size/image specifics.

Documentation

Full guides live in docs/:

Repository layout

docker/                 Dockerfile (pinned Terraform + Ansible)
compose.yaml            single "iac" tooling service
run.ps1 / Makefile      wrappers:  .\run.ps1 deploy -Cloud hetzner
.env.example            all provider tokens in one place
terraform/
  modules/
    digitalocean/ hetzner/ aws/ gcp/ azure/ linode/   compute + firewall per cloud
    dns-cloudflare/                                   provider-agnostic DNS
    ansible-inventory/                                writes ansible/inventory/<cloud>.ini
  stacks/<cloud>/        thin root that wires the modules (own state, own providers)
ansible/
  playbook.yml
  roles/wireguard/       idempotent WireGuard install/config
  inventory/             generated per cloud (gitignored)

Each stack keeps its own state and its own provider set, so deploying to Hetzner never asks for AWS credentials, and you can switch clouds freely.

Prerequisites

  • Docker (Desktop on Windows/macOS, or Engine on Linux)
  • An SSH key pair (the public key goes to the cloud; the private key lets Ansible connect)
  • A Cloudflare-managed domain (for DNS) — or set dns_enabled = false

Quick start

  1. Configure secrets

    cp .env.example .env

    Fill in the token(s) for the cloud you're targeting, CLOUDFLARE_API_TOKEN, and SSH_DIR (the host folder holding your private key — e.g. C:\Users\you\.ssh). It is mounted read-only into the container at /root/.ssh.

  2. (Per-cloud) set variables

    cp terraform/stacks/<cloud>/terraform.tfvars.example terraform/stacks/<cloud>/terraform.tfvars

    For DigitalOcean the defaults work out of the box. Hetzner/AWS/etc. need ssh_public_key. Also point Ansible at your key: ssh_private_key_file = "/root/.ssh/id_ed25519".

  3. Build the toolchain image (once)

    .\run.ps1 build              # or: make build
  4. Deploy — provision, write inventory, then configure WireGuard:

    .\run.ps1 deploy -Cloud digitalocean
    # Linux/macOS:  make deploy CLOUD=digitalocean
  5. Get the server IP

    .\run.ps1 output -Cloud digitalocean

Individual steps

.\run.ps1 init      -Cloud hetzner   # terraform init
.\run.ps1 plan      -Cloud hetzner   # terraform plan
.\run.ps1 apply     -Cloud hetzner   # terraform apply
.\run.ps1 configure -Cloud hetzner   # ansible-playbook (WireGuard)
.\run.ps1 destroy   -Cloud hetzner   # tear it down
.\run.ps1 shell                      # drop into the tooling container

DNS

Each stack defaults to a distinct Cloudflare record so multiple clouds can coexist: *.0 (DO), *.hz, *.aws, *.gcp, *.az, *.ln under dns_zone (default xeze.org). Override dns_record_name per stack, or set dns_enabled = false to skip DNS entirely. The record is never proxied (Cloudflare's proxy doesn't forward WireGuard's UDP).

Adding more WireGuard clients

Edit wg_clients in ansible/roles/wireguard/defaults/main.yml, give each a unique /32 in the 10.0.0.0/24 range, then re-run configure. Keys are generated once and never regenerated. Client configs land in /etc/wireguard/clients/ on the server. Show one as a QR code for mobile:

ssh <user>@<ip> 'qrencode -t ansiutf8 < /etc/wireguard/clients/client1.conf'

State

State is local per stack (mounted into the container, so it persists). For teams or durability, switch to a remote backend — see the commented example in each stack's backend.tf, then run terraform init -migrate-state.

Adding a new provider

  1. Create terraform/modules/<cloud>/ with main.tf, variables.tf, outputs.tf.
  2. Honor the contract — inputs name/region/size/image/wireguard_port/... and outputs server_ipv4, server_ipv6, server_id, ssh_user.
  3. Add a thin terraform/stacks/<cloud>/ that wires modules/<cloud> + dns-cloudflare
    • ansible-inventory (copy an existing stack).
  4. Add the provider's credential vars to .env.example.

The Ansible role needs no changes — it only requires SSH to a Debian/Ubuntu host.

License

MIT

About

This Terraform configuration sets up a hybrid cloud infrastructure by deploying a WireGuard VPN server on DigitalOcean. This allows secure connectivity between your on-premises network and cloud resources, enabling a true hybrid cloud environment.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages