diff --git a/servers/legion/apps/headscale.yaml b/servers/legion/apps/headscale.yaml new file mode 100644 index 0000000..17f33da --- /dev/null +++ b/servers/legion/apps/headscale.yaml @@ -0,0 +1,113 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: headscale-config + namespace: headscale +data: + config.yaml: | + server_url: https://headscale.neuralplatform.ai + listen_addr: 0.0.0.0:8080 + grpc_listen_addr: 0.0.0.0:50443 + grpc_allow_insecure: true + private_key_path: /data/private.key + noise: + private_key_path: /data/noise_private.key + ip_prefixes: + - 100.64.0.0/10 + db_type: sqlite3 + db_path: /data/headscale.db + log: + level: info + dns_config: + nameservers: + - 100.64.0.1 + magic_dns: true + base_domain: vpn.neuralplatform.ai + derp: + server: + enabled: false + urls: + - https://controlplane.tailscale.com/derpmap/default + auto_update_enabled: true + update_frequency: 24h + disable_check_updates: true + ephemeral_node_inactivity_timeout: 30m + node_update_check_interval: 10s +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: headscale + namespace: headscale + labels: + app: headscale +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: headscale + template: + metadata: + labels: + app: headscale + spec: + containers: + - name: headscale + image: headscale/headscale:0.23 + command: ["headscale", "serve"] + ports: + - name: http + containerPort: 8080 + - name: grpc + containerPort: 50443 + volumeMounts: + - name: data + mountPath: /data + - name: config + mountPath: /etc/headscale/config.yaml + subPath: config.yaml + resources: + requests: + memory: 64Mi + cpu: 50m + limits: + memory: 256Mi + cpu: 200m + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 10 + periodSeconds: 30 + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + volumes: + - name: data + persistentVolumeClaim: + claimName: headscale-data + - name: config + configMap: + name: headscale-config +--- +apiVersion: v1 +kind: Service +metadata: + name: headscale + namespace: headscale +spec: + selector: + app: headscale + ports: + - name: http + port: 8080 + targetPort: 8080 + - name: grpc + port: 50443 + targetPort: 50443 + type: ClusterIP diff --git a/servers/legion/headscale.tf b/servers/legion/headscale.tf new file mode 100644 index 0000000..fe2bf7f --- /dev/null +++ b/servers/legion/headscale.tf @@ -0,0 +1,33 @@ +# Headscale — self-hosted Tailscale control plane +# Coordination endpoint: https://headscale.neuralplatform.ai (Cloudflare tunnel) +# Clients: Tailscale app on Mac/iOS/Android, pointed at custom server +# Subnet router: tailscaled on Legion host, advertises k3s + LAN CIDRs + +resource "kubernetes_namespace" "headscale" { + metadata { + name = "headscale" + labels = { + managed-by = "terraform" + tier = "infrastructure" + } + } +} + +resource "kubernetes_persistent_volume_claim" "headscale_data" { + metadata { + name = "headscale-data" + namespace = kubernetes_namespace.headscale.metadata[0].name + } + + # wait_until_bound = false because local-path only provisions on pod schedule + wait_until_bound = false + + spec { + access_modes = ["ReadWriteOnce"] + resources { + requests = { + storage = "1Gi" + } + } + } +}