Adds headscale app.
This commit is contained in:
5
headscale/app.yaml
Normal file
5
headscale/app.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
name: headscale
|
||||||
|
is: headscale
|
||||||
|
description: Headscale is a self-hosted, open-source implementation of the Tailscale control server, enabling you to manage your own private WireGuard-based mesh VPN network.
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/headscale.svg
|
||||||
|
latest: "1"
|
||||||
65
headscale/versions/1/README.md
Normal file
65
headscale/versions/1/README.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# Headscale
|
||||||
|
|
||||||
|
Headscale is an open-source, self-hosted implementation of the Tailscale control plane. It lets you run your own private WireGuard-based mesh VPN using the Tailscale client.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Key settings in `config.yaml`:
|
||||||
|
|
||||||
|
- **domain** - Where the Headscale control plane API is accessible
|
||||||
|
- **vpnBaseDomain** - Base domain for MagicDNS hostnames assigned to nodes (default: `vpn.{your-cloud-domain}`)
|
||||||
|
- **storage** - Persistent volume size (default: `2Gi`)
|
||||||
|
|
||||||
|
## First-Time Setup
|
||||||
|
|
||||||
|
1. Add and deploy the app:
|
||||||
|
```bash
|
||||||
|
wild app add headscale
|
||||||
|
wild app deploy headscale
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Create a user (namespace) in Headscale:
|
||||||
|
```bash
|
||||||
|
kubectl exec -n headscale deploy/headscale -- headscale users create <username>
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Generate a pre-auth key for that user:
|
||||||
|
```bash
|
||||||
|
kubectl exec -n headscale deploy/headscale -- headscale preauthkeys create --user <user-id> --reusable --expiration 24h
|
||||||
|
```
|
||||||
|
Note: `--user` requires the numeric user ID, not the username. Run `headscale users list` to get the ID.
|
||||||
|
|
||||||
|
4. On each device you want to join the VPN, install the Tailscale client and connect it to your Headscale instance:
|
||||||
|
```bash
|
||||||
|
tailscale up --login-server https://<your-headscale-domain> --authkey <preauthkey>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Registering Nodes via Browser Auth
|
||||||
|
|
||||||
|
If a Tailscale client connects without a pre-auth key, it shows an auth URL in its logs. Register that node with:
|
||||||
|
```bash
|
||||||
|
kubectl exec -n headscale deploy/headscale -- headscale nodes register --auth-id <hskey-authreq-XXXX> --user <username>
|
||||||
|
```
|
||||||
|
Note: `--user` here accepts the username string (not the numeric ID).
|
||||||
|
|
||||||
|
## Useful Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List users
|
||||||
|
kubectl exec -n headscale deploy/headscale -- headscale users list
|
||||||
|
|
||||||
|
# List connected nodes
|
||||||
|
kubectl exec -n headscale deploy/headscale -- headscale nodes list
|
||||||
|
|
||||||
|
# Create a pre-auth key (requires numeric user ID)
|
||||||
|
kubectl exec -n headscale deploy/headscale -- headscale preauthkeys create --user <id> --reusable
|
||||||
|
|
||||||
|
# Delete a node
|
||||||
|
kubectl exec -n headscale deploy/headscale -- headscale nodes delete --identifier <node-id>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Headscale uses SQLite by default — no external database dependency
|
||||||
|
- Configuration is managed via a ConfigMap (`headscale-config`) — changes require restarting the pod
|
||||||
|
- In Headscale v0.29+, different commands accept different user identifier types: `preauthkeys` and `users destroy` require the numeric ID, while `nodes register` accepts the username string
|
||||||
88
headscale/versions/1/configmap.yaml
Normal file
88
headscale/versions/1/configmap.yaml
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: headscale-config
|
||||||
|
namespace: {{ .namespace }}
|
||||||
|
data:
|
||||||
|
config.yaml: |
|
||||||
|
server_url: "https://{{ .domain }}"
|
||||||
|
listen_addr: "0.0.0.0:8080"
|
||||||
|
grpc_listen_addr: "0.0.0.0:50443"
|
||||||
|
grpc_allow_insecure: false
|
||||||
|
metrics_listen_addr: "127.0.0.1:9090"
|
||||||
|
|
||||||
|
# Trust forwarded IPs from Traefik ingress
|
||||||
|
trusted_proxies:
|
||||||
|
- "10.0.0.0/8"
|
||||||
|
- "172.16.0.0/12"
|
||||||
|
- "192.168.0.0/16"
|
||||||
|
|
||||||
|
log:
|
||||||
|
level: info
|
||||||
|
format: text
|
||||||
|
|
||||||
|
database:
|
||||||
|
type: sqlite
|
||||||
|
sqlite:
|
||||||
|
path: /var/lib/headscale/db.sqlite
|
||||||
|
write_ahead_log: true
|
||||||
|
wal_autocheckpoint: 1000
|
||||||
|
|
||||||
|
policy:
|
||||||
|
mode: file
|
||||||
|
path: /etc/headscale/acls.hujson
|
||||||
|
|
||||||
|
tls_letsencrypt_hostname: ""
|
||||||
|
tls_cert_path: ""
|
||||||
|
tls_key_path: ""
|
||||||
|
|
||||||
|
noise:
|
||||||
|
private_key_path: /var/lib/headscale/noise_private.key
|
||||||
|
|
||||||
|
prefixes:
|
||||||
|
v6: fd7a:115c:a1e0::/48
|
||||||
|
v4: 100.64.0.0/10
|
||||||
|
allocation: sequential
|
||||||
|
|
||||||
|
derp:
|
||||||
|
server:
|
||||||
|
enabled: false
|
||||||
|
region_id: 999
|
||||||
|
region_code: headscale
|
||||||
|
region_name: Headscale Embedded DERP
|
||||||
|
stun_listen_addr: "0.0.0.0:3478"
|
||||||
|
private_key_path: /var/lib/headscale/derp_server_private.key
|
||||||
|
automatically_add_embedded_derp_region: true
|
||||||
|
urls:
|
||||||
|
- https://controlplane.tailscale.com/derpmap/default
|
||||||
|
paths: []
|
||||||
|
auto_update_enabled: true
|
||||||
|
update_frequency: 24h
|
||||||
|
|
||||||
|
dns:
|
||||||
|
magic_dns: true
|
||||||
|
base_domain: {{ .vpnBaseDomain }}
|
||||||
|
override_local_dns: true
|
||||||
|
nameservers:
|
||||||
|
global:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 1.0.0.1
|
||||||
|
search_domains: []
|
||||||
|
|
||||||
|
node:
|
||||||
|
ephemeral:
|
||||||
|
inactivity_timeout: 30m
|
||||||
|
|
||||||
|
unix_socket: /var/lib/headscale/headscale.sock
|
||||||
|
unix_socket_permission: "0770"
|
||||||
|
|
||||||
|
acls.hujson: |
|
||||||
|
{
|
||||||
|
"acls": [
|
||||||
|
{
|
||||||
|
"action": "accept",
|
||||||
|
"src": ["*"],
|
||||||
|
"dst": ["*:*"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
73
headscale/versions/1/deployment.yaml
Normal file
73
headscale/versions/1/deployment.yaml
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: headscale
|
||||||
|
namespace: {{ .namespace }}
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
component: server
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
component: server
|
||||||
|
spec:
|
||||||
|
securityContext:
|
||||||
|
runAsNonRoot: false
|
||||||
|
runAsUser: 0
|
||||||
|
seccompProfile:
|
||||||
|
type: RuntimeDefault
|
||||||
|
containers:
|
||||||
|
- name: headscale
|
||||||
|
image: headscale/headscale:v0.29.1
|
||||||
|
args: ["serve"]
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 8080
|
||||||
|
protocol: TCP
|
||||||
|
- name: grpc
|
||||||
|
containerPort: 50443
|
||||||
|
protocol: TCP
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 256Mi
|
||||||
|
requests:
|
||||||
|
cpu: 50m
|
||||||
|
memory: 128Mi
|
||||||
|
volumeMounts:
|
||||||
|
- name: headscale-config
|
||||||
|
mountPath: /etc/headscale
|
||||||
|
- name: headscale-data
|
||||||
|
mountPath: /var/lib/headscale
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 30
|
||||||
|
failureThreshold: 3
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
failureThreshold: 3
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
readOnlyRootFilesystem: false
|
||||||
|
volumes:
|
||||||
|
- name: headscale-config
|
||||||
|
configMap:
|
||||||
|
name: headscale-config
|
||||||
|
- name: headscale-data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: headscale-data
|
||||||
|
restartPolicy: Always
|
||||||
26
headscale/versions/1/ingress.yaml
Normal file
26
headscale/versions/1/ingress.yaml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: headscale
|
||||||
|
namespace: headscale
|
||||||
|
annotations:
|
||||||
|
external-dns.alpha.kubernetes.io/target: {{ .externalDnsDomain }}
|
||||||
|
external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"
|
||||||
|
external-dns.alpha.kubernetes.io/ttl: "60"
|
||||||
|
spec:
|
||||||
|
ingressClassName: traefik
|
||||||
|
rules:
|
||||||
|
- host: {{ .domain }}
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: headscale
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- {{ .domain }}
|
||||||
|
secretName: {{ .tlsSecretName }}
|
||||||
16
headscale/versions/1/kustomization.yaml
Normal file
16
headscale/versions/1/kustomization.yaml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
namespace: headscale
|
||||||
|
labels:
|
||||||
|
- includeSelectors: true
|
||||||
|
pairs:
|
||||||
|
app: headscale
|
||||||
|
managedBy: kustomize
|
||||||
|
partOf: wild-cloud
|
||||||
|
resources:
|
||||||
|
- namespace.yaml
|
||||||
|
- configmap.yaml
|
||||||
|
- pvc.yaml
|
||||||
|
- deployment.yaml
|
||||||
|
- service.yaml
|
||||||
|
- ingress.yaml
|
||||||
19
headscale/versions/1/manifest.yaml
Normal file
19
headscale/versions/1/manifest.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
version: 0.29.1-1
|
||||||
|
scripts:
|
||||||
|
- name: register-node
|
||||||
|
description: Register a Tailscale node using the auth ID shown in the Tailscale app.
|
||||||
|
path: scripts/register-node.sh
|
||||||
|
params:
|
||||||
|
- name: AUTH_ID
|
||||||
|
description: The auth ID shown by the Tailscale app (e.g. hskey-authreq-...)
|
||||||
|
required: true
|
||||||
|
- name: USER
|
||||||
|
description: Headscale username to register the node under
|
||||||
|
required: true
|
||||||
|
defaultConfig:
|
||||||
|
namespace: headscale
|
||||||
|
domain: headscale.{{ .cloud.domain }}
|
||||||
|
externalDnsDomain: "{{ .cloud.domain }}"
|
||||||
|
tlsSecretName: wildcard-wild-cloud-tls
|
||||||
|
storage: 2Gi
|
||||||
|
vpnBaseDomain: vpn.{{ .cloud.domain }}
|
||||||
4
headscale/versions/1/namespace.yaml
Normal file
4
headscale/versions/1/namespace.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: "{{ .namespace }}"
|
||||||
11
headscale/versions/1/pvc.yaml
Normal file
11
headscale/versions/1/pvc.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: headscale-data
|
||||||
|
namespace: headscale
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: {{ .storage }}
|
||||||
18
headscale/versions/1/scripts/register-node.sh
Executable file
18
headscale/versions/1/scripts/register-node.sh
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ -z "$AUTH_ID" ]; then
|
||||||
|
echo "Error: AUTH_ID is required"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$USER" ]; then
|
||||||
|
echo "Error: USER is required"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
NAMESPACE=$(cat manifest.yaml | grep 'namespace:' | head -1 | awk '{print $2}')
|
||||||
|
|
||||||
|
echo "Registering node for user '$USER' with auth ID: $AUTH_ID"
|
||||||
|
kubectl exec -n "$NAMESPACE" deploy/headscale -- \
|
||||||
|
headscale auth register --auth-id "$AUTH_ID" --user "$USER"
|
||||||
18
headscale/versions/1/service.yaml
Normal file
18
headscale/versions/1/service.yaml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: headscale
|
||||||
|
namespace: headscale
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
protocol: TCP
|
||||||
|
targetPort: 8080
|
||||||
|
- name: grpc
|
||||||
|
port: 50443
|
||||||
|
protocol: TCP
|
||||||
|
targetPort: 50443
|
||||||
|
selector:
|
||||||
|
component: server
|
||||||
Reference in New Issue
Block a user