From 2cd8a249b6beff780ed862eeea9b68533b6750d2 Mon Sep 17 00:00:00 2001 From: Khue Doan Date: Thu, 18 Apr 2024 20:11:18 +0700 Subject: [PATCH] refactor!: replace ZeroTier with Tailscale and Wireguard - Tailscale UX is better, and the Headscale control server is also easier to self-host than ZeroTier (although Headscale is not the official control server, the author now works at Tailscale) - Wireguard is also added as an alternative to avoid relying on a third-party service, however it requires port-forwarding --- README.md | 17 +++-- apps/homepage/values.yaml | 3 - docs/getting-started/vpn-setup.md | 61 +++++++++++++++++ .../production/external-resources.md | 11 ---- external/main.tf | 8 --- external/modules/zerotier/main.tf | 57 ---------------- external/modules/zerotier/variables.tf | 34 ---------- external/modules/zerotier/versions.tf | 13 ---- external/namespaces.yml | 1 - external/terraform.tfvars.example | 3 - external/variables.tf | 4 -- mkdocs.yml | 1 + platform/zerotier/deployment.yaml | 65 ------------------- platform/zerotier/kustomization.yaml | 5 -- scripts/get-wireguard-config | 10 +++ 15 files changed, 83 insertions(+), 210 deletions(-) create mode 100644 docs/getting-started/vpn-setup.md delete mode 100644 external/modules/zerotier/main.tf delete mode 100644 external/modules/zerotier/variables.tf delete mode 100644 external/modules/zerotier/versions.tf delete mode 100644 platform/zerotier/deployment.yaml delete mode 100644 platform/zerotier/kustomization.yaml create mode 100755 scripts/get-wireguard-config diff --git a/README.md b/README.md index bdca1bc6..762cd205 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ More information can be found in [the roadmap](#roadmap) below. - [x] Modular architecture, easy to add or remove features/components - [x] Automated certificate management - [x] Automatically update DNS records for exposed services -- [x] VPN without port forwarding +- [x] VPN (Tailscale or Wireguard) - [x] Expose services to the internet securely with [Cloudflare Tunnel](https://www.cloudflare.com/products/tunnel/) - [x] CI/CD platform - [x] Private container registry @@ -207,16 +207,21 @@ They can't capture all the project's features, but they are sufficient to get a Rook Ceph Cloud-Native Storage for Kubernetes + + + Tailscale + VPN without port forwarding + + + + Wireguard + Fast, modern, secure VPN tunnel + Woodpecker CI Simple yet powerful CI/CD engine with great extensibility - - - ZeroTier - VPN without port forwarding - Zot Registry diff --git a/apps/homepage/values.yaml b/apps/homepage/values.yaml index bdbbd106..0183f727 100644 --- a/apps/homepage/values.yaml +++ b/apps/homepage/values.yaml @@ -142,9 +142,6 @@ app-template: - Terraform Cloud: - href: https://app.terraform.io icon: terraform.svg - - Zerotier: - - href: https://my.zerotier.com - icon: zerotier.svg - Infrastructure: - Router: - href: https://192.168.1.1 diff --git a/docs/getting-started/vpn-setup.md b/docs/getting-started/vpn-setup.md new file mode 100644 index 00000000..4ab57300 --- /dev/null +++ b/docs/getting-started/vpn-setup.md @@ -0,0 +1,61 @@ +# VPN setup + +You can choose between [Tailscale](https://tailscale.com), [Wireguard](https://www.wireguard.com), or use both like me. + +## Tailscale (requires third-party account) + +Get an [auth key](https://tailscale.com/kb/1085/auth-keys) from [Tailscale admin console](https://login.tailscale.com/admin/authkeys): + +- Description: homelab +- Reusable: optionally set this to true + +Add it to `external/terraform.tfvars` as an extra secret: + +```hcl +extra_secrets = { + tailscale-auth-key = "tskey-auth-myauthkeyhere" +} +``` + +Apply the secret: + +```sh +make external +``` + +Finally, [enable subnet routes](https://tailscale.com/kb/1019/subnets#step-3-enable-subnet-routes-from-the-admin-console) for `homelab-router` +from the [admin console](https://login.tailscale.com/admin/machines). + +You can now connect to your homelab via Tailscale and [invite user to your Tailscale network](https://tailscale.com/kb/1371/invite-users). + +## Wireguard (requires port-forwarding) + +Update the peer list in `apps/wireguard/values.yaml`: + +```yaml +PEERS: | + UserDevice + FooPhone + FooLaptop + BarDesktop +``` + +Go to your router settings and forward the Wireguard service. +Each router is different, here's mine for reference: + +- Protocol: `UDP` +- Start Port: `51820` +- End Port: `51820` +- Local IP Address: `192.168.1.226` (find it with `kubectl get service -n wireguard wireguard`) +- Start Port Local: `51820` +- End Port Local: `51820` + +To get the QR code (for mobile) and config (for desktop), run: + +!!! warning + + This command will print sensitive secrets to the terminal. + +```sh +./scripts/get-wireguard-config FooPhone +``` diff --git a/docs/installation/production/external-resources.md b/docs/installation/production/external-resources.md index d1bd374c..161c1688 100644 --- a/docs/installation/production/external-resources.md +++ b/docs/installation/production/external-resources.md @@ -12,7 +12,6 @@ Below is a list of external resources and why we need them (also see some [alter | Terraform Cloud | Workspace | Terraform state backend | | Cloudflare | DNS | DNS and [DNS-01 challenge](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge) for certificates | | Cloudflare | Tunnel | Public services to the internet without port forwarding | -| ZeroTier | Virtual network | Use as VPN to access home network from anywhere (with [UDP hole punching](https://en.wikipedia.org/wiki/UDP_hole_punching)) | | ntfy | Topic | External notification service to receive alerts | @@ -51,11 +50,6 @@ If you decide to use a [different Terraform backend](https://www.terraform.io/la -### ZeroTier - -- Create a ZeroTier account -- Generate a new API Token at - @@ -82,11 +76,6 @@ To avoid vendor lock-in, each external provider must have an equivalent alternat - Create a small VPS in the cloud and utilize Wireguard to route traffic via it - Access everything via VPN - See also [awesome tunneling](https://github.com/anderspitman/awesome-tunneling) -- ZeroTier virtual network: - - [Host your own ZeroTier](https://docs.zerotier.com/self-hosting/introduction) - - [Tailscale](https://tailscale.com) (closed source, but you can use [Headscale](https://github.com/juanfont/headscale) to host your own Tailscale control server) - - [Netmaker](https://www.netmaker.org) (there's no hosted version, you'll need to host your own server) - - Wireguard server (requires port forwarding) - ntfy: - [Self-host your own ntfy server](https://docs.ntfy.sh/install) - Any other [integration supported by Grafana Alerting](https://grafana.com/docs/grafana/latest/alerting/alerting-rules/manage-contact-points/integrations/#list-of-supported-integrations) diff --git a/external/main.tf b/external/main.tf index fad7148a..b4f9c2b0 100644 --- a/external/main.tf +++ b/external/main.tf @@ -5,14 +5,6 @@ module "cloudflare" { cloudflare_api_key = var.cloudflare_api_key } -module "zerotier" { - source = "./modules/zerotier" - zerotier_central_token = var.zerotier_central_token - bridged_routes = [ - "192.168.1.224/27" - ] -} - module "ntfy" { source = "./modules/ntfy" auth = var.ntfy diff --git a/external/modules/zerotier/main.tf b/external/modules/zerotier/main.tf deleted file mode 100644 index 89c11e95..00000000 --- a/external/modules/zerotier/main.tf +++ /dev/null @@ -1,57 +0,0 @@ -locals { - router_ip = cidrhost(var.managed_route, 1) # Use the second IP in the VPN subnet as the router -} - -resource "zerotier_network" "network" { - name = var.name - description = var.description - private = true - - route { - target = var.managed_route - } - - dynamic "route" { - for_each = var.bridged_routes - - content { - target = route.value - via = local.router_ip - } - } - - assignment_pool { - start = cidrhost(var.managed_route, 0) - end = cidrhost(var.managed_route, -1) - } -} - -resource "zerotier_identity" "router" {} - -resource "zerotier_member" "router" { - network_id = zerotier_network.network.id - name = "router" - member_id = zerotier_identity.router.id - allow_ethernet_bridging = true - no_auto_assign_ips = true - ip_assignments = [ - local.router_ip - ] -} - -resource "kubernetes_secret" "router" { - metadata { - name = "zerotier-router" - namespace = "zerotier" - - annotations = { - "app.kubernetes.io/managed-by" = "Terraform" - } - } - - data = { - ZEROTIER_NETWORK_ID = zerotier_network.network.id - ZEROTIER_IDENTITY_PUBLIC = zerotier_identity.router.public_key - ZEROTIER_IDENTITY_SECRET = zerotier_identity.router.private_key - } -} diff --git a/external/modules/zerotier/variables.tf b/external/modules/zerotier/variables.tf deleted file mode 100644 index e7a2bc27..00000000 --- a/external/modules/zerotier/variables.tf +++ /dev/null @@ -1,34 +0,0 @@ -variable "zerotier_central_url" { - description = "ZeroTier Central API endpoint" - type = string - default = "https://my.zerotier.com/api" # https://github.com/zerotier/go-ztcentral/blob/4d397d1e82c043a5376789177ad55536044d69ce/client.go#L44 -} - -variable "zerotier_central_token" { - description = "ZeroTier Central API Token" - type = string - sensitive = true -} - -variable "name" { - description = "Network name" - type = string - default = "homelab" -} - -variable "description" { - description = "Network description" - type = string - default = "Homelab network" -} - -variable "managed_route" { - description = "ZeroTier managed route" - type = string - default = "10.147.17.0/24" -} - -variable "bridged_routes" { - description = "List of bridged routes" # TODO - type = list(string) -} diff --git a/external/modules/zerotier/versions.tf b/external/modules/zerotier/versions.tf deleted file mode 100644 index 88a1cbfa..00000000 --- a/external/modules/zerotier/versions.tf +++ /dev/null @@ -1,13 +0,0 @@ -terraform { - required_providers { - zerotier = { - source = "zerotier/zerotier" - version = "~> 1.4.0" - } - } -} - -provider "zerotier" { - zerotier_central_url = var.zerotier_central_url - zerotier_central_token = var.zerotier_central_token -} diff --git a/external/namespaces.yml b/external/namespaces.yml index 794d5713..e5f03917 100644 --- a/external/namespaces.yml +++ b/external/namespaces.yml @@ -13,4 +13,3 @@ - global-secrets - k8up-operator - monitoring-system - - zerotier diff --git a/external/terraform.tfvars.example b/external/terraform.tfvars.example index 4a6aed1b..aabb2101 100644 --- a/external/terraform.tfvars.example +++ b/external/terraform.tfvars.example @@ -5,9 +5,6 @@ cloudflare_account_id = "foobarid" # https://dash.cloudflare.com/profile/api-tokens cloudflare_api_key = "foobarkey" -# https://my.zerotier.com/account -zerotier_central_token = "foobartoken" - ntfy = { # https://ntfy.sh or your own instance url = "https://ntfy.sh" diff --git a/external/variables.tf b/external/variables.tf index e244231c..de59f0d2 100644 --- a/external/variables.tf +++ b/external/variables.tf @@ -11,10 +11,6 @@ variable "cloudflare_account_id" { type = string } -variable "zerotier_central_token" { - type = string -} - variable "ntfy" { type = object({ url = string diff --git a/mkdocs.yml b/mkdocs.yml index e65af92e..276411e3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -47,6 +47,7 @@ nav: - installation/production/deployment.md - installation/post-installation.md - Getting started: + - getting-started/vpn-setup.md - getting-started/user-onboarding.md - getting-started/install-pre-commit-hooks.md - Concepts: diff --git a/platform/zerotier/deployment.yaml b/platform/zerotier/deployment.yaml deleted file mode 100644 index a3c89d5d..00000000 --- a/platform/zerotier/deployment.yaml +++ /dev/null @@ -1,65 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: zerotier-router - namespace: zerotier - labels: - app: zerotier-router -spec: - selector: - matchLabels: - app: zerotier-router - replicas: 1 - template: - metadata: - labels: - app: zerotier-router - spec: - containers: - - name: zerotier-router - image: zerotier/zerotier:latest # TODO use tag - command: - - "sh" - - "-c" - args: # TODO optimize this - - | - # TODO install this on upstream image? - apt-get install -y iptables - - # TODO is there a better way to get the interface name? - export PHY_IFACE="$(ip route | grep ${POD_IP} | cut -d ' ' -f 3)" - export ZT_IFACE=zt0 - - # Override the default random interface name - mkdir -p /var/lib/zerotier-one - echo "${ZEROTIER_NETWORK_ID}=${ZT_IFACE}" >> /var/lib/zerotier-one/devicemap - - iptables -t nat -A POSTROUTING -o $PHY_IFACE -j MASQUERADE - iptables -A FORWARD -i $PHY_IFACE -o $ZT_IFACE -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -A FORWARD -i $ZT_IFACE -o $PHY_IFACE -j ACCEPT - - /entrypoint.sh "${ZEROTIER_NETWORK_ID}" - resources: - requests: - cpu: 100m - memory: 128Mi - env: - - name: POD_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - envFrom: - - secretRef: - name: zerotier-router - securityContext: - capabilities: - add: - - NET_ADMIN - volumeMounts: - - name: dev-net-tun - mountPath: /dev/net/tun - volumes: - - name: dev-net-tun - hostPath: - path: /dev/net/tun - type: CharDevice diff --git a/platform/zerotier/kustomization.yaml b/platform/zerotier/kustomization.yaml deleted file mode 100644 index 88a04b54..00000000 --- a/platform/zerotier/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -resources: - - deployment.yaml diff --git a/scripts/get-wireguard-config b/scripts/get-wireguard-config new file mode 100755 index 00000000..87f402cd --- /dev/null +++ b/scripts/get-wireguard-config @@ -0,0 +1,10 @@ +#!/bin/sh + +set -eu + +PEER="${1}" + +export KUBECONFIG=./metal/kubeconfig.yaml + +kubectl -n wireguard exec -it deployment/wireguard -- /app/show-peer "${PEER}" +kubectl -n wireguard exec -it deployment/wireguard -- cat "/config/peer_${PEER}/peer_${PEER}.conf"