diff --git a/kubernetes/headplane/Chart.yaml b/kubernetes/headplane/Chart.yaml new file mode 100644 index 00000000..d58657f7 --- /dev/null +++ b/kubernetes/headplane/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +appVersion: 0.6.1 +description: Kubernetes Helm Chart for Headplane in integrated mode +name: headplane +type: application +version: 0.6.1 diff --git a/kubernetes/headplane/templates/_helpers.tpl b/kubernetes/headplane/templates/_helpers.tpl new file mode 100644 index 00000000..3ce47d38 --- /dev/null +++ b/kubernetes/headplane/templates/_helpers.tpl @@ -0,0 +1,7 @@ +{{- define "headplane.cookieSecret" -}} +{{- if .Values.headplane.config.cookieSecret.value -}} +{{- .Values.headplane.config.cookieSecret.value -}} +{{- else -}} +{{- randAlphaNum 32 -}} +{{- end -}} +{{- end -}} diff --git a/kubernetes/headplane/templates/deployment.yaml b/kubernetes/headplane/templates/deployment.yaml new file mode 100644 index 00000000..8d07fc9b --- /dev/null +++ b/kubernetes/headplane/templates/deployment.yaml @@ -0,0 +1,166 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: headplane-integrated +spec: + replicas: 1 + selector: + matchLabels: + app: headplane-integrated + strategy: + type: Recreate + template: + metadata: + labels: + app: headplane-integrated + annotations: + checksum/configmap-headplane: {{ include (print $.Template.BasePath "/headplane/configmap.yaml") . | sha256sum }} + checksum/secret-headplane: {{ include (print $.Template.BasePath "/headplane/secrets.yaml") . | sha256sum }} + checksum/secret-headscale: {{ include (print $.Template.BasePath "/headscale/secret.yaml") . | sha256sum }} + spec: + hostAliases: {{- toYaml .Values.hostAliases | nindent 8 }} + shareProcessNamespace: true + serviceAccountName: headplane-integrated + securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: headplane + image: "{{ required "headplane image repository required" .Values.headplane.image.repository }}:{{ .Values.headplane.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ required "headplane image pull policy required" .Values.headplane.image.pullPolicy }} + env: + - name: HEADPLANE_DEBUG_LOG + value: {{ .Values.headplane.config.debug | quote }} + - name: HEADPLANE_LOAD_ENV_OVERRIDES + value: "true" + - name: HEADPLANE_INTEGRATION__KUBERNETES__POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + ports: + - name: app + containerPort: 3000 + protocol: TCP + securityContext: {{- toYaml .Values.headplane.securityContext | nindent 12 }} + volumeMounts: + - name: headplane-config + mountPath: /etc/headplane + - name: headplane-server-cookie-secret + mountPath: /var/secrets/headplane/server + {{- if .Values.headplane.config.oidc.enabled }} + - name: headplane-oidc-client-secret + mountPath: /var/secrets/headplane/oidc + - name: headplane-oidc-headscale-api-key + mountPath: /var/secrets/headplane/headscale + {{- end }} + - name: headplane-data + mountPath: /var/lib/headplane + readOnly: false + - name: headscale-config + mountPath: /etc/headscale + initContainers: + - name: headscale + image: "{{ required "headscale image repository required" .Values.headscale.image.repository }}:{{ required "headscale image tag required" .Values.headscale.image.tag }}" + imagePullPolicy: {{ required "headscale image pull policy required" .Values.headscale.image.pullPolicy }} + restartPolicy: Always + args: + - serve + ports: + - name: api + containerPort: 8080 + protocol: TCP + - name: metrics + containerPort: 9090 + protocol: TCP + - name: grpc + containerPort: 50443 + protocol: TCP + - name: stun + containerPort: 3478 + protocol: UDP + securityContext: {{- toYaml .Values.headscale.securityContext | nindent 12 }} + volumeMounts: + - name: headscale-config + mountPath: /etc/headscale + {{- if .Values.headscale.config.oidc.enabled }} + - name: headscale-oidc-client-secret + mountPath: /var/secrets/headscale/oidc + {{- end }} + - name: headscale-data + mountPath: /var/lib/headscale + readOnly: false + {{- if .Values.headplane.config.generateCredentials }} + - name: generate-headscale-token + image: alpine/k8s:1.33.1 + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: HEADSCALE_SECRET_NAME + value: {{ default "headscale-api-key" .Values.headplane.config.oidc.headscaleApiKey.secretName }} + - name: HEADSCALE_POD_SELECTOR + value: "app=headplane-integrated" + command: [ "bash", "-c" ] + args: [ "/etc/scripts/ensure-headscale-api-key.sh" ] + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + volumeMounts: + - name: headplane-scripts + mountPath: /etc/scripts + {{- end }} + volumes: + ### + ### Headplane Volumes ### + ### + - name: headplane-config + configMap: + name: headplane + - name: headplane-server-cookie-secret + secret: + secretName: {{ default "headplane-cookie-secret" .Values.headplane.config.cookieSecret.secretName }} + {{- if .Values.headplane.config.oidc.enabled }} + - name: headplane-oidc-client-secret + secret: + secretName: {{ default "headplane-oidc-client-secret" .Values.headplane.config.oidc.clientSecret.secretName }} + - name: headplane-oidc-headscale-api-key + secret: + secretName: {{ default "headscale-api-key" .Values.headplane.config.oidc.headscaleApiKey.secretName }} + {{- end }} + - name: headplane-data + {{- if .Values.headplane.persistence.enabled }} + persistentVolumeClaim: + claimName: headplane-data + {{- else }} + emptyDir: + sizeLimit: 500Mi + {{- end }} + {{- if .Values.headplane.config.generateCredentials }} + - name: headplane-scripts + configMap: + name: headplane-scripts + defaultMode: 0777 + {{- end }} + ### + ### Headscale Volumes ### + ### + - name: headscale-config + configMap: + name: headscale + {{- if .Values.headscale.config.oidc.enabled }} + - name: headscale-oidc-client-secret + secret: + secretName: {{ default "headscale-oidc-client-secret" .Values.headscale.config.oidc.clientSecret.secretName }} + {{- end }} + - name: headscale-data + {{- if .Values.headscale.persistence.enabled }} + persistentVolumeClaim: + claimName: headscale-data + {{- else }} + emptyDir: + sizeLimit: 500Mi + {{- end }} diff --git a/kubernetes/headplane/templates/extra-objects.yaml b/kubernetes/headplane/templates/extra-objects.yaml new file mode 100644 index 00000000..fc9a76b8 --- /dev/null +++ b/kubernetes/headplane/templates/extra-objects.yaml @@ -0,0 +1,8 @@ +{{ range .Values.extraObjects }} +--- +{{ if typeIs "string" . }} + {{- tpl . $ }} +{{- else }} + {{- tpl (toYaml .) $ }} +{{- end }} +{{ end }} diff --git a/kubernetes/headplane/templates/headplane/configmap-scripts.yaml b/kubernetes/headplane/templates/headplane/configmap-scripts.yaml new file mode 100644 index 00000000..617d3ef7 --- /dev/null +++ b/kubernetes/headplane/templates/headplane/configmap-scripts.yaml @@ -0,0 +1,132 @@ +{{- if .Values.headplane.config.generateCredentials }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: headplane-scripts +data: + ensure-headscale-api-key.sh: | + set -e + + NAMESPACE="${NAMESPACE:?Error: NAMESPACE environment variable not set.}" + HEADSCALE_SECRET_NAME="${HEADSCALE_SECRET_NAME:?Error: HEADSCALE_SECRET_NAME environment variable not set.}" + HEADSCALE_POD_SELECTOR="${HEADSCALE_POD_SELECTOR:?Error: HEADSCALE_POD_SELECTOR environment variable not set.}" + + check_api_key_validity() { + local key_to_check="$1" + local validation_url="$HEADSCALE_HOST:8080/api/v1/user" + + local http_status + local curl_stderr_output + local curl_exit_status + + curl_stderr_output=$(curl -sS --fail -o /dev/null -w "%{http_code}\n" -H "Authorization: Bearer $key_to_check" "$validation_url" 2>&1) + curl_exit_status=$? + + http_status=$(echo "$curl_stderr_output" | tail -n 1) + curl_stderr_output=$(echo "$curl_stderr_output" | head -n -1) + + if [[ "$curl_exit_status" -eq 0 && "$http_status" =~ ^2 ]]; then + echo "Headscale API Key is valid (HTTP $http_status OK)." + return 0 + else + echo "API Key validation failed." + echo "Debug Info:" + echo " Validation URL: $validation_url" + echo " Curl Exit Status: $curl_exit_status" + echo " HTTP Status Code: $http_status" + if [[ -n "$curl_stderr_output" ]]; then + echo " Curl Error Output: $curl_stderr_output" + fi + return 1 + fi + } + + echo "Finding headscale pod in namespace '$NAMESPACE' with selector '$HEADSCALE_POD_SELECTOR'..." + HEADPLANE_POD=$(kubectl get pod -n "$NAMESPACE" -l "$HEADSCALE_POD_SELECTOR" -o jsonpath="{.items[0].metadata.name}" --ignore-not-found) + + if [[ -z "$HEADPLANE_POD" ]]; then + echo "Error: No headscale pod found matching selector '$HEADSCALE_POD_SELECTOR' in namespace '$NAMESPACE'." + exit 1 + fi + echo "Success: Found headscale pod '$HEADPLANE_POD'" + + + echo "Checking 'headscale' container status in pod '$HEADPLANE_POD' for readiness..." + + container_ready=$(kubectl get pod -n "$NAMESPACE" "$HEADPLANE_POD" -o jsonpath="{.status.initContainerStatuses[?(@.name==\"headscale\")].ready}" 2>/dev/null || echo "") + + if [[ "$container_ready" == "true" ]]; then + echo "Success: 'headscale' container is ready" + else + echo "--- Headscale Container Readiness Check Failed ---" + echo "Error: 'headscale' container in pod '$HEADPLANE_POD' is NOT ready." + if [[ -z "$container_ready" ]]; then + echo " Reason: Container status not yet available (Pod might be starting or in a pending state)." + else + echo " Reason: Container status found, but reported as '$container_ready'." + fi + echo " Namespace: '$NAMESPACE'" + echo " Pod Name: '$HEADPLANE_POD'" + echo "---------------------------------------------------------" + exit 1 + fi + + if [[ -z "${HEADSCALE_HOST:-}" ]]; then + echo "HEADSCALE_HOST environment variable not provided. Attempting to determine pod IP for pod '$HEADPLANE_POD'..." + POD_IP=$(kubectl get pod -n $NAMESPACE $HEADPLANE_POD -o jsonpath='{.status.podIP}' --ignore-not-found) + + if [[ -z "$POD_IP" ]]; then + POD_IP="127.0.0.1" + echo "Could not retrieve IP for pod '$HEADPLANE_POD'. Using default." + fi + + HEADSCALE_HOST="http://$POD_IP" + echo "HEADSCALE_HOST set to: '$HEADSCALE_HOST'" + fi + + API_KEY="" + echo "Checking for existing Kubernetes secret '$HEADSCALE_SECRET_NAME' in namespace '$NAMESPACE'..." + + ENCODED_KEY_DATA=$(kubectl get secret "$HEADSCALE_SECRET_NAME" -n "$NAMESPACE" -o=jsonpath='{.data.api-key}' --ignore-not-found 2>/dev/null || echo "") + + if [[ -n "$ENCODED_KEY_DATA" ]]; then + API_KEY=$(echo "$ENCODED_KEY_DATA" | base64 -d) + echo "Existing Headscale API Key found in secret '$HEADSCALE_SECRET_NAME'." + + if check_api_key_validity "$API_KEY"; then + echo "Existing Headscale API Key is valid. No further action needed." + exit 0 + else + echo "Existing Headscale API Key is invalid. A new API Key will be generated." + fi + else + echo "Kubernetes secret '$HEADSCALE_SECRET_NAME' not found or does not contain a 'api-key' field. A new API Key will be generated." + fi + + echo "Generating a new Headscale API Key by executing CLI inside 'headscale' container..." + API_KEY=$(kubectl exec -n "$NAMESPACE" -c headscale "$HEADPLANE_POD" -- headscale apikeys create -e 100y) + + if [[ -z "$API_KEY" ]]; then + echo "Error: Failed to create a new API Key via 'headscale apikeys create' command." + exit 1 + fi + echo "Successfully generated a new Headscale API Key." + + if kubectl get secret "$HEADSCALE_SECRET_NAME" -n "$NAMESPACE" &>/dev/null; then + echo "Updating existing secret '$HEADSCALE_SECRET_NAME' with the new API Key..." + kubectl patch secret "$HEADSCALE_SECRET_NAME" -n "$NAMESPACE" -p "{\"stringData\":{\"api-key\":\"$API_KEY\"}}" --type=merge + else + echo "Creating new secret '$HEADSCALE_SECRET_NAME' with the new API Key..." + kubectl create secret generic "$HEADSCALE_SECRET_NAME" -n "$NAMESPACE" --from-literal="api-key=$API_KEY" + fi + echo "Successfully ensured Headscale API Key in Kubernetes secret '$HEADSCALE_SECRET_NAME'." + + echo "--- Performing final validation of the newly generated API Key ---" + if check_api_key_validity "$API_KEY"; then + echo "Final validation successful: The newly generated and stored Headscale API Key is valid." + exit 0 + else + echo "Final validation failed: The newly generated/stored Headscale API Key is NOT valid. Please investigate." + exit 1 + fi +{{- end }} \ No newline at end of file diff --git a/kubernetes/headplane/templates/headplane/configmap.yaml b/kubernetes/headplane/templates/headplane/configmap.yaml new file mode 100644 index 00000000..24452a45 --- /dev/null +++ b/kubernetes/headplane/templates/headplane/configmap.yaml @@ -0,0 +1,33 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: headplane +data: + config.yaml: | + server: + host: 0.0.0.0 + port: 3000 + cookie_secret_path: /var/secrets/headplane/server/{{ .Values.headplane.config.cookieSecret.secretKey }} + cookie_secure: true + headscale: + url: http://127.0.0.1:8080 + {{- if .Values.headscale.config.url }} + public_url: {{ .Values.headscale.config.url }} + {{- end }} + config_path: /etc/headscale/config.yaml + config_strict: true + integration: + kubernetes: + enabled: true + validate_manifest: true + pod_name: replaced-by-environment-variable + {{- if .Values.headplane.config.oidc.enabled }} + oidc: + issuer: {{ .Values.headplane.config.oidc.issuerUrl }} + client_id: {{ .Values.headplane.config.oidc.clientId }} + client_secret_path: /var/secrets/headplane/oidc/{{ .Values.headplane.config.oidc.clientSecret.secretKey }} + disable_api_key_login: {{ .Values.headplane.config.oidc.disableApiKeyLogin }} + token_endpoint_auth_method: {{ .Values.headplane.config.oidc.tokenEndpointAuthMethod }} + headscale_api_key_path: /var/secrets/headplane/headscale/{{ .Values.headplane.config.oidc.headscaleApiKey.secretKey }} + redirect_uri: {{ .Values.headplane.config.url }}/admin/oidc/callback + {{- end }} diff --git a/kubernetes/headplane/templates/headplane/pvc.yaml b/kubernetes/headplane/templates/headplane/pvc.yaml new file mode 100644 index 00000000..f1082391 --- /dev/null +++ b/kubernetes/headplane/templates/headplane/pvc.yaml @@ -0,0 +1,23 @@ +{{- if .Values.headplane.persistence.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: headplane-data + {{- with .Values.headplane.persistence.pvc.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.headplane.persistence.pvc.labels }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + accessModes: + {{- toYaml .Values.headplane.persistence.pvc.accessModes | nindent 4 }} + resources: + requests: + storage: {{ .Values.headplane.persistence.pvc.storage }} + {{- if .Values.headplane.persistence.pvc.storageClassName }} + storageClassName: {{ .Values.headplane.persistence.pvc.storageClassName }} + {{- end }} +{{- end }} diff --git a/kubernetes/headplane/templates/headplane/secrets.yaml b/kubernetes/headplane/templates/headplane/secrets.yaml new file mode 100644 index 00000000..3fe4143f --- /dev/null +++ b/kubernetes/headplane/templates/headplane/secrets.yaml @@ -0,0 +1,29 @@ +{{ if not .Values.headplane.config.cookieSecret.secretName }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: headplane-cookie-secret +stringData: + cookie-secret: {{ include "headplane.cookieSecret" . }} +{{- end }} +{{- if and .Values.headplane.config.oidc.enabled }} +{{- if not .Values.headplane.config.oidc.clientSecret.secretName }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: headplane-oidc-client-secret +stringData: + client-secret: {{ required "headplane plaintext client secret value required" .Values.headplane.config.oidc.clientSecret.value }} +{{- end }} +{{- if (and (not .Values.headplane.config.oidc.headscaleApiKey.secretName) (not .Values.headplane.config.generateCredentials)) }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: headscale-api-key +stringData: + api-key: {{ required "headplane plaintext headscale api key required" .Values.headplane.config.oidc.headscaleApiKey.value }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/kubernetes/headplane/templates/headplane/service.yaml b/kubernetes/headplane/templates/headplane/service.yaml new file mode 100644 index 00000000..ae64b017 --- /dev/null +++ b/kubernetes/headplane/templates/headplane/service.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: headplane +spec: + ports: + - name: headplane-ui + port: 80 + protocol: TCP + targetPort: app + selector: + app: headplane-integrated + type: ClusterIP diff --git a/kubernetes/headplane/templates/headscale/configmap.yaml b/kubernetes/headplane/templates/headscale/configmap.yaml new file mode 100644 index 00000000..df49ecbb --- /dev/null +++ b/kubernetes/headplane/templates/headscale/configmap.yaml @@ -0,0 +1,72 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: headscale +data: + config.yaml: | + server_url: {{ .Values.headscale.config.url }} + listen_addr: 0.0.0.0:8080 + metrics_listen_addr: 127.0.0.1:9090 + grpc_listen_addr: 127.0.0.1:50443 + grpc_allow_insecure: false + noise: + private_key_path: /var/lib/headscale/noise_private.key + prefixes: + v4: 100.64.0.0/10 + v6: fd7a:115c:a1e0::/48 + allocation: sequential + derp: + server: + enabled: false + urls: + - https://controlplane.tailscale.com/derpmap/default + paths: [] + auto_update_enabled: true + update_frequency: 24h + disable_check_updates: true + ephemeral_node_inactivity_timeout: 30m + database: + type: sqlite + gorm: + prepare_stmt: true + parameterized_queries: true + skip_err_record_not_found: true + slow_threshold: 1000 + sqlite: + path: /var/lib/headscale/db.sqlite + write_ahead_log: true + wal_autocheckpoint: 1000 + log: + format: text + level: info + policy: + mode: database + dns: + magic_dns: {{ .Values.headscale.config.dns.magicDns }} + base_domain: {{ .Values.headscale.config.dns.baseDomain }} + {{- if .Values.headscale.config.dns.nameservers }} + nameservers: + {{- if .Values.headscale.config.dns.nameservers.global }} + global: {{- toYaml .Values.headscale.config.dns.nameservers.global | nindent 10 }} + {{- end }} + {{- if .Values.headscale.config.dns.nameservers.split }} + split: {{- toYaml .Values.headscale.config.dns.nameservers.split | nindent 10 }} + {{- end }} + {{- end }} + unix_socket: /var/lib/headscale/headscale.sock + unix_socket_permission: "0770" + {{- if .Values.headscale.config.oidc.enabled }} + oidc: + only_start_if_oidc_is_available: {{ .Values.headscale.config.oidc.startupCheck }} + issuer: {{ .Values.headscale.config.oidc.issuerUrl }} + client_id: {{ .Values.headscale.config.oidc.clientId }} + client_secret_path: /var/secrets/headscale/oidc/client-secret + expiry: 180d + use_expiry_from_token: false + pkce: + enabled: {{ .Values.headscale.config.oidc.pkceEnabled }} + method: S256 + {{- end }} + logtail: + enabled: false + randomize_client_port: false diff --git a/kubernetes/headplane/templates/headscale/pvc.yaml b/kubernetes/headplane/templates/headscale/pvc.yaml new file mode 100644 index 00000000..00685c4a --- /dev/null +++ b/kubernetes/headplane/templates/headscale/pvc.yaml @@ -0,0 +1,23 @@ +{{- if .Values.headscale.persistence.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: headscale-data + {{- with .Values.headscale.persistence.pvc.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.headscale.persistence.pvc.labels }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + accessModes: + {{- toYaml .Values.headscale.persistence.pvc.accessModes | nindent 4 }} + resources: + requests: + storage: {{ .Values.headscale.persistence.pvc.storage }} + {{- if .Values.headscale.persistence.pvc.storageClassName }} + storageClassName: {{ .Values.headscale.persistence.pvc.storageClassName }} + {{- end }} +{{- end }} diff --git a/kubernetes/headplane/templates/headscale/secret.yaml b/kubernetes/headplane/templates/headscale/secret.yaml new file mode 100644 index 00000000..3af0f77c --- /dev/null +++ b/kubernetes/headplane/templates/headscale/secret.yaml @@ -0,0 +1,8 @@ +{{ if and .Values.headscale.config.oidc.enabled (not .Values.headscale.config.oidc.clientSecret.secretName) }} +apiVersion: v1 +kind: Secret +metadata: + name: headscale-oidc-client-secret +stringData: + client-secret: {{ required "headscale plaintext secret value required" .Values.headscale.config.oidc.clientSecret.value }} +{{- end }} \ No newline at end of file diff --git a/kubernetes/headplane/templates/headscale/service.yaml b/kubernetes/headplane/templates/headscale/service.yaml new file mode 100644 index 00000000..70b89413 --- /dev/null +++ b/kubernetes/headplane/templates/headscale/service.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: headscale +spec: + ports: + - name: headscale-api + port: 80 + protocol: TCP + targetPort: api + selector: + app: headplane-integrated + type: ClusterIP diff --git a/kubernetes/headplane/templates/role.yaml b/kubernetes/headplane/templates/role.yaml new file mode 100644 index 00000000..42bb327f --- /dev/null +++ b/kubernetes/headplane/templates/role.yaml @@ -0,0 +1,17 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: headplane-integrated +rules: +- apiGroups: ['apps'] + resources: ['deployments'] + verbs: ['get', 'list'] +- apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "watch"] +- apiGroups: [""] + resources: ["pods/exec"] + verbs: ["create"] +- apiGroups: [""] + resources: ["secrets"] + verbs: ["create", "patch", "get"] \ No newline at end of file diff --git a/kubernetes/headplane/templates/rolebinding.yaml b/kubernetes/headplane/templates/rolebinding.yaml new file mode 100644 index 00000000..888c060e --- /dev/null +++ b/kubernetes/headplane/templates/rolebinding.yaml @@ -0,0 +1,11 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: headplane-integrated +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: headplane-integrated +subjects: +- kind: ServiceAccount + name: headplane-integrated \ No newline at end of file diff --git a/kubernetes/headplane/templates/serviceaccount.yaml b/kubernetes/headplane/templates/serviceaccount.yaml new file mode 100644 index 00000000..6d5c4f1f --- /dev/null +++ b/kubernetes/headplane/templates/serviceaccount.yaml @@ -0,0 +1,5 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: headplane-integrated \ No newline at end of file diff --git a/kubernetes/headplane/values.yaml b/kubernetes/headplane/values.yaml new file mode 100644 index 00000000..4b90d17f --- /dev/null +++ b/kubernetes/headplane/values.yaml @@ -0,0 +1,102 @@ +headplane: + image: + repository: ghcr.io/tale/headplane + pullPolicy: IfNotPresent + tag: "" + config: + url: https://headplane.example.com + debug: false + generateCredentials: false + cookieSecret: + value: "" + secretName: ~ + secretKey: "cookie-secret" + oidc: + enabled: false + issuerUrl: "" + disableApiKeyLogin: false + tokenEndpointAuthMethod: client_secret_post + redirectUri: "" + clientId: "" + clientSecret: + value: "" + secretName: ~ + secretKey: "client-secret" + headscaleApiKey: + value: "" + secretName: ~ + secretKey: "api-key" + persistence: + enabled: false + pvc: + enabled: false + name: headplane-data + accessModes: + - ReadWriteOnce + storage: 1Gi + annotations: {} + labels: [] + storageClassName: ~ + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + ingress: + enabled: false + className: "" + annotations: [] + labels: [] + gateway: + enabled: false +headscale: + image: + repository: headscale/headscale + pullPolicy: IfNotPresent + tag: "0.26.1" + config: + url: https://headscale.example.com + dns: + magicDns: true + baseDomain: example.com + nameservers: + global: + - 9.9.9.9 + - 149.112.112.112 + - 2620:fe::fe + - 2620:fe::9 + split: {} + oidc: + enabled: true + issuerUrl: "" + startupCheck: true + pkceEnabled: true + clientId: "" + clientSecret: + value: "" + secretName: ~ + secretKey: "client-secret" + persistence: + enabled: false + pvc: + enabled: false + name: headscale-data + accessModes: + - ReadWriteOnce + storage: 1Gi + annotations: {} + labels: [] + storageClassName: ~ + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 +podSecurityContext: + fsGroup: 2000 +extraObjects: [] +hostAliases: []