DEV Community

Diego Segura
Diego Segura

Posted on

How to Deploy a Hardened Firezone (WireGuard) + Classic IPsec VPN on Google Cloud with Terraform πŸš€

Why This Baseline Helps πŸ’‘

If you're managing remote access for a distributed team and need site-to-site connectivity with partners running legacy IPsec, you've probably felt the pain of maintaining two separate VPN stacks. One modern (WireGuard via Firezone), one classic (strongSwan/Libreswan), both fighting for the same public IP and firewall rules.

This guide walks you through a single Terraform deployment that gives you:

  • Two access patterns, one deployment: WireGuard for your remote users, Classic IPsec for partner networks.
  • Zero secrets in git: All credentials live in GCP Secret Manager, referenced via data sources.
  • Production-ready from day one: Load balancer health checks, automated backups, OpenSSF Scorecard hardening, and cost-saving VM schedulers.

By the end, you'll have a working VPN gateway on GCP that your team can connect to via the Firezone mobile/desktop clients, while your partners connect via standard IPsec tunnelsβ€”all managed as code.


What You're Building πŸ—οΈ

Here's the architecture at a glance:

Component Purpose Key Features
Firezone Modern WireGuard VPN with web UI User management, device enrollment, audit logs
strongSwan (optional) Classic IPsec for site-to-site tunnels Partner compatibility, IKEv2/ESP support
GCP Load Balancer Health-checked traffic routing Automatic failover, regional redundancy
Cloud Scheduler Cost optimization Auto-start 6am, auto-stop 11pm (configurable)
Secret Manager Credential storage Zero plaintext secrets in code

Prerequisites Checklist βœ…

Before you start, make sure you have:

  • GCP Project with billing enabled
  • Terraform v1.9+ installed locally
  • gcloud CLI authenticated (gcloud auth application-default login)
  • Service Account with roles:
    • roles/compute.instanceAdmin.v1
    • roles/cloudscheduler.admin

Note: If you plan to enable site-to-site VPN tunnels, you'll also need to create secrets in GCP Secret Manager for the IPsec pre-shared keys (format: vpn-psk-{tunnel_name}). See the VPN Gateway module documentation for details.


Step 1: Clone and Configure πŸ“¦

git clone https://github.com/dieguezz/terraform-gcp-vpn.git
cd terraform-gcp-vpn
cp terraform.tfvars.example terraform.tfvars
Enter fullscreen mode Exit fullscreen mode

Edit terraform.tfvars with your values:

project_id = "your-gcp-project-id"
region     = "us-central1"
zone       = "us-central1-a"

# Network
network_name    = "vpn-network"
subnet_name     = "vpn-subnet"
subnet_cidr     = "10.128.0.0/20"

# Firezone
firezone_hostname = "vpn.yourdomain.com"

# Scheduling (optional, remove to run 24/7)
enable_scheduling = true
start_schedule    = "0 6 * * 1-5"  # 6am Mon-Fri
stop_schedule     = "0 23 * * 1-5" # 11pm Mon-Fri
timezone          = "America/New_York"
Enter fullscreen mode Exit fullscreen mode

Step 2: Initialize Terraform πŸ”§

terraform init
Enter fullscreen mode Exit fullscreen mode

You'll see modules downloading:

Initializing modules...
- compute in modules/compute
- load_balancer in modules/load-balancer
- network in modules/network
- scheduler in modules/scheduler
- security in modules/security
- vpn_gateway in modules/vpn-gateway
Enter fullscreen mode Exit fullscreen mode

Step 3: Review the Plan πŸ‘€

terraform plan
Enter fullscreen mode Exit fullscreen mode

Terraform will show you what's about to be created. Pay attention to:

  • Compute Engine instance (e2-medium with 50GB boot disk)
  • VPC network and subnet with firewall rules for UDP 51820, 500, 4500
  • Load balancer with health check on Firezone port 13000
  • Cloud Functions for start/stop scheduling (if enable_scheduling = true)
  • IAM bindings for Secret Manager access

Step 4: Deploy 🚒

terraform apply
Enter fullscreen mode Exit fullscreen mode

Type yes when prompted. The deployment takes ~5 minutes. Coffee break time β˜•


Step 5: Access Firezone 🌐

Once complete, Terraform outputs the load balancer IP:

Outputs:

firezone_url = "https://35.xxx.xxx.xxx"
load_balancer_ip = "35.xxx.xxx.xxx"
vpn_instance_name = "firezone-vpn-instance"
Enter fullscreen mode Exit fullscreen mode
  1. Point your DNS: Create an A record for vpn.yourdomain.com β†’ 35.xxx.xxx.xxx
  2. Wait 2-3 minutes for Firezone to initialize (check startup script logs with gcloud compute instances get-serial-port-output)
  3. Open browser: Navigate to https://vpn.yourdomain.com
  4. Login: Use your Google Workspace credentials (configured via firezone_admin_email and google_workspace_domain variables)
  5. Enroll devices: Add users, generate WireGuard configs via the Firezone UI

Step 6: Site-to-Site VPN (Optional) πŸ”—

This deployment also supports Classic IPsec tunnels for connecting with partner networks or legacy infrastructure. This is completely optional and disabled by default.

If you need this capability, the infrastructure includes a pre-configured strongSwan setup. To enable it:

  • Set enable_site_to_site_vpn = true in your terraform.tfvars
  • Configure your tunnel parameters in the vpn_tunnels variable
  • Create the required pre-shared keys in Secret Manager

For detailed configuration steps, see the VPN Gateway Module Documentation which includes complete examples and troubleshooting guides.


Security Hardening Applied πŸ”’

This deployment follows OpenSSF Scorecard recommendations:

  • βœ… No secrets in code – all credentials in Secret Manager
  • βœ… Pinned dependencies – GitHub Actions use SHA hashes
  • βœ… SAST enabled – CodeQL scans on every PR
  • βœ… Least-privilege IAM – service account has minimal roles
  • βœ… Encrypted boot disk – GCP default encryption at rest
  • βœ… Regular backups – VM snapshots via Cloud Scheduler (roadmap item)

Cost Breakdown (Monthly Estimate) πŸ’°

Resource Type Cost (USD/month)
Compute Engine e2-medium (50% usage with scheduler) ~$12
Load Balancer Network LB with health checks ~$18
VPC Egress traffic (10GB assumed) ~$1
Secret Manager 4 secrets, 10 accesses/day <$1
Cloud Scheduler 2 jobs (start/stop) Free tier
Total ~$32/month

Savings: Without the scheduler (24/7 runtime), the e2-medium costs ~$24/month. The scheduler saves you ~$12/month (50% reduction).


Troubleshooting πŸ”

Symptom Likely Cause Fix
terraform apply fails with "secret not found" VPN tunnel enabled but PSK secret missing Create secret: gcloud secrets create vpn-psk-{tunnel_name} or disable site-to-site VPN
Can't access Firezone UI after 5 min Startup script still running Check gcloud compute instances get-serial-port-output for errors
WireGuard clients can't connect Firewall rules not applied Verify gcloud compute firewall-rules list includes allow-wireguard
Google Workspace SSO not working Domain mismatch or OAuth misconfigured Verify google_workspace_domain matches your workspace and check Firezone OAuth settings
Instance stops unexpectedly Scheduler enabled but wrong timezone Verify timezone variable in terraform.tfvars

What's Next? 🎯

Now that you have a working baseline:

  • Add users: Use the Firezone web UI to invite team members and generate device configs
  • Monitor traffic: Enable VPC Flow Logs (gcloud compute networks subnets update --enable-flow-logs)
  • Automate backups: Extend the scheduler module to snapshot the boot disk weekly
  • Multi-region failover: Deploy a second instance in another region, use Cloud DNS routing
  • Audit logs: Forward Firezone logs to Cloud Logging for compliance

Key Takeaways πŸ“

  • Hybrid VPN stacks don't have to be messy. One Terraform deployment can handle both modern WireGuard and legacy IPsec.
  • Zero secrets in git is achievable. GCP Secret Manager + Terraform data sources = clean commit history.
  • Cost control matters. A simple scheduler can cut your VM bill in half for dev/staging environments.
  • Security is a journey. OpenSSF Scorecard gives you a roadmapβ€”automated fixes get you 80% of the way.

Repository & Resources πŸ“š

Got questions or want to share your VPN war stories? Drop a comment below or open an issue on GitHub. If this helped you, a ⭐ on the repo keeps me caffeinated for the next guide!


Next in series: Architecture Deep Dive – How the Modules Talk to Each Other (and Why It Matters) πŸ›οΈ

Top comments (0)