diff --git a/README.md b/README.md index e237f2c1..9fab5f85 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ The cluster-deployment tools here include helm charts and ansible playbooks to s | Service | Version | Notes | | --- | --- | --- | +| apache | ** | web server, with webdav example | | authelia | ** | single-signon multi-factor auth | | data-sync | [![](https://img.shields.io/docker/v/instantlinux/data-sync?sort=date)](https://hub.docker.com/r/instantlinux/data-sync "Version badge") | poor-man's SAN for persistent storage | | ddclient | [![](https://img.shields.io/docker/v/instantlinux/ddclient?sort=date)](https://hub.docker.com/r/instantlinux/ddclient "Version badge") | Dynamic DNS client | @@ -68,6 +69,7 @@ The cluster-deployment tools here include helm charts and ansible playbooks to s | haproxy-keepalived | [![](https://img.shields.io/docker/v/instantlinux/haproxy-keepalived?sort=date)](https://hub.docker.com/r/instantlinux/haproxy-keepalived "Version badge") | load balancer | | grafana | ** | monitoring dashboard with prometheus-based alerting | | guacamole | ** | authenticated remote-desktop server | +| headscale | ** | tailscale-compatible VPN control-plane | | mysqldump | [![](https://img.shields.io/docker/v/instantlinux/mysqldump?sort=date)](https://hub.docker.com/r/instantlinux/mysqldump "Version badge") | per-database alternative to xtrabackup | | nagios | [![](https://img.shields.io/docker/v/instantlinux/nagios?sort=date)](https://hub.docker.com/r/instantlinux/nagios "Version badge") | Nagios Core v4 for monitoring | | nagiosql | [![](https://img.shields.io/docker/v/instantlinux/nagiosql?sort=date)](https://hub.docker.com/r/instantlinux/nagiosql "Version badge") | NagiosQL for configuring Nagios Core v4 | @@ -76,6 +78,7 @@ The cluster-deployment tools here include helm charts and ansible playbooks to s | nut-upsd | [![](https://img.shields.io/docker/v/instantlinux/nut-upsd?sort=date)](https://hub.docker.com/r/instantlinux/nut-upsd "Version badge") | Network UPS Tools | | openldap | [![](https://img.shields.io/docker/v/instantlinux/openldap?sort=date)](https://hub.docker.com/r/instantlinux/openldap "Version badge") | OpenLDAP authentication server | | proftpd | [![](https://img.shields.io/docker/v/instantlinux/proftpd?sort=date)](https://hub.docker.com/r/instantlinux/proftpd "Version badge") | FTP server | +| radicale | ** | CalDAV / CardDAV for mobile calendar/contacts sync | | restic | ** | backups | | rsyslogd | [![](https://img.shields.io/docker/v/instantlinux/rsyslogd?sort=date)](https://hub.docker.com/r/instantlinux/rsyslogd "Version badge") | logger in a 13MB image | | samba | [![](https://img.shields.io/docker/v/instantlinux/samba?sort=date)](https://hub.docker.com/r/instantlinux/samba "Version badge") | file server | diff --git a/k8s/helm/headscale/.helmignore b/k8s/helm/headscale/.helmignore new file mode 100644 index 00000000..839de881 --- /dev/null +++ b/k8s/helm/headscale/.helmignore @@ -0,0 +1,2 @@ +*~ +.git diff --git a/k8s/helm/headscale/Chart.yaml b/k8s/helm/headscale/Chart.yaml new file mode 100644 index 00000000..2955cbff --- /dev/null +++ b/k8s/helm/headscale/Chart.yaml @@ -0,0 +1,18 @@ +apiVersion: v2 +name: headscale +description: Headscale control server for wireshark / tailscale VPN +home: https://github.com/instantlinux/docker-tools +sources: +- https://github.com/instantlinux/docker-tools +- https://github.com/juanfont/headscale +type: application +version: 0.1.0 +appVersion: "0.29.3" +dependencies: +- name: chartlib + version: 0.1.8 + repository: https://instantlinux.github.io/docker-tools +- name: headplane + version: 0.1.0 + repository: file://subcharts/headplane + condition: headplane.enabled diff --git a/k8s/helm/headscale/subcharts/headplane/Chart.yaml b/k8s/helm/headscale/subcharts/headplane/Chart.yaml new file mode 100644 index 00000000..e6e004e5 --- /dev/null +++ b/k8s/helm/headscale/subcharts/headplane/Chart.yaml @@ -0,0 +1,15 @@ +apiVersion: v2 +name: headplane +description: UI for headscale +home: https://github.com/instantlinux/docker-tools +sources: +- https://github.com/instantlinux/docker-tools +- https://github.com/tale/headplane +type: application +version: 0.1.0 +# specify version tag from ghcr.io in top-level values.yaml +appVersion: "0.0.1" +dependencies: +- name: chartlib + version: 0.1.8 + repository: https://instantlinux.github.io/docker-tools diff --git a/k8s/helm/headscale/subcharts/headplane/templates/NOTES.txt b/k8s/helm/headscale/subcharts/headplane/templates/NOTES.txt new file mode 100644 index 00000000..62ea3f4b --- /dev/null +++ b/k8s/helm/headscale/subcharts/headplane/templates/NOTES.txt @@ -0,0 +1,28 @@ +{{- if hasKey .Values "service" }} +{{- if or .Values.service.enabled (not (hasKey .Values.service "enabled")) }} +1. Get the application URL by running these commands: +{{- if hasKey .Values "ingress" }} +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "local.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "local.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "local.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "local.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/k8s/helm/headscale/subcharts/headplane/templates/app.yaml b/k8s/helm/headscale/subcharts/headplane/templates/app.yaml new file mode 100644 index 00000000..5a01911b --- /dev/null +++ b/k8s/helm/headscale/subcharts/headplane/templates/app.yaml @@ -0,0 +1,15 @@ +{{- include "chartlib.configmap" . }} +--- +{{- include "chartlib.deployment" . }} +--- +{{- include "chartlib.hpa" . }} +--- +{{- include "chartlib.ingress" . }} +--- +{{- include "chartlib.ingresstotp" . }} +--- +{{- include "chartlib.service" . }} +--- +{{- include "chartlib.serviceaccount" . }} +--- +{{- include "chartlib.statefulset" . }} diff --git a/k8s/helm/headscale/subcharts/headplane/templates/configmap.yaml b/k8s/helm/headscale/subcharts/headplane/templates/configmap.yaml new file mode 100644 index 00000000..846a4909 --- /dev/null +++ b/k8s/helm/headscale/subcharts/headplane/templates/configmap.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "local.fullname" . }} + labels: + {{- include "local.labels" . | nindent 4 }} +data: + config.yaml: | + server: + host: 0.0.0.0 + port: 3000 + base_url: {{ .Values.base_url }} + cookie_secret_path: /etc/headplane/cookie_secret + cookie_secure: true + headscale: + url: {{ .Values.headscale_url }} + config_path: /etc/headscale/config.yaml + kubernetes: + enabled: true diff --git a/k8s/helm/headscale/subcharts/headplane/templates/tests/test-connection.yaml b/k8s/helm/headscale/subcharts/headplane/templates/tests/test-connection.yaml new file mode 100644 index 00000000..ae159a4f --- /dev/null +++ b/k8s/helm/headscale/subcharts/headplane/templates/tests/test-connection.yaml @@ -0,0 +1,17 @@ +{{- if hasKey .Values "service" }} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "local.fullname" . }}-test-connection" + labels: + {{- include "local.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "local.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never +{{- end }} diff --git a/k8s/helm/headscale/subcharts/headplane/values.yaml b/k8s/helm/headscale/subcharts/headplane/values.yaml new file mode 100644 index 00000000..47bbe127 --- /dev/null +++ b/k8s/helm/headscale/subcharts/headplane/values.yaml @@ -0,0 +1,50 @@ +# Default values for headplane +base_url: http://localhost:3000 +headscale_url: headscale.example.com +deployment: + containerPorts: [ containerPort: 3000 ] +volumeMounts: +- mountPath: /etc/headplane/config.yaml + name: config + subPath: config.yaml +- mountPath: /etc/headplane/cookie_secret + name: cookie-secret + readOnly: true + subPath: cookie_secret +- mountPath: /etc/headscale/config.yaml + name: headscale-config + subPath: config.yaml +volumes: +- name: config + configMap: + name: headscale-headplane +- name: headscale-config + configMap: + name: headscale +- name: cookie-secret + secret: + secretName: headscale + items: + - key: cookie_secret + path: cookie_secret + +image: + repository: ghcr.io/tale/headplane + pullPolicy: IfNotPresent + # tag: default + +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + enabled: false +service: + clusterIP: None + ports: + - { port: 3000, targetPort: 3000, name: headscale } + type: ClusterIP +autoscaling: + enabled: false + +ingress: + enabled: false diff --git a/k8s/helm/headscale/templates/NOTES.txt b/k8s/helm/headscale/templates/NOTES.txt new file mode 100644 index 00000000..62ea3f4b --- /dev/null +++ b/k8s/helm/headscale/templates/NOTES.txt @@ -0,0 +1,28 @@ +{{- if hasKey .Values "service" }} +{{- if or .Values.service.enabled (not (hasKey .Values.service "enabled")) }} +1. Get the application URL by running these commands: +{{- if hasKey .Values "ingress" }} +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "local.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "local.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "local.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "local.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/k8s/helm/headscale/templates/app.yaml b/k8s/helm/headscale/templates/app.yaml new file mode 100644 index 00000000..5a01911b --- /dev/null +++ b/k8s/helm/headscale/templates/app.yaml @@ -0,0 +1,15 @@ +{{- include "chartlib.configmap" . }} +--- +{{- include "chartlib.deployment" . }} +--- +{{- include "chartlib.hpa" . }} +--- +{{- include "chartlib.ingress" . }} +--- +{{- include "chartlib.ingresstotp" . }} +--- +{{- include "chartlib.service" . }} +--- +{{- include "chartlib.serviceaccount" . }} +--- +{{- include "chartlib.statefulset" . }} diff --git a/k8s/helm/headscale/templates/tests/test-connection.yaml b/k8s/helm/headscale/templates/tests/test-connection.yaml new file mode 100644 index 00000000..ae159a4f --- /dev/null +++ b/k8s/helm/headscale/templates/tests/test-connection.yaml @@ -0,0 +1,17 @@ +{{- if hasKey .Values "service" }} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "local.fullname" . }}-test-connection" + labels: + {{- include "local.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "local.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never +{{- end }} diff --git a/k8s/helm/headscale/values.yaml b/k8s/helm/headscale/values.yaml new file mode 100644 index 00000000..31637782 --- /dev/null +++ b/k8s/helm/headscale/values.yaml @@ -0,0 +1,122 @@ +# Default values for headscale. + +tlsHostname: headscale.example.com +deployment: + command: [ headscale, serve ] + containerPorts: + - containerPort: 8080 + - containerPort: 9090 + resources: + limits: + memory: 512Mi + requests: + cpu: 100m + memory: 64Mi +volumeMounts: +- name: config + mountPath: /etc/headscale/config.yaml + subPath: config.yaml +- name: share + mountPath: /var/lib/headscale + subPath: headscale +volumes: +- name: config + configMap: + name: headscale +- name: share + hostPath: { path: /var/lib/docker/k8s-volumes/share } + +image: + repository: headscale/headscale + pullPolicy: IfNotPresent + # tag: default + +nameOverride: "" +fullnameOverride: "" + +serviceAccount: {} +service: + clusterIP: None + ports: + - { port: 8080, targetPort: 8080, name: headscale } + type: ClusterIP + +ingress: + enabled: true + className: "" + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/enable-access-log: "false" + rules: + - host: headscale.example.com + http: + paths: + - path: /admin + backend: + service: + name: headscale-headplane + port: + number: 3000 + pathType: Prefix + - path: / + backend: + service: + name: headscale + port: + number: 8080 + pathType: Prefix + +autoscaling: + enabled: false + +configmap: + data: + config.yaml: | + server_url: https://headscale.example.com + listen_addr: 0.0.0.0:8080 + metrics_listen_addr: 0.0.0.0:9090 + noise: + private_key_path: /var/lib/headscale/noise_private.key + prefixes: + v4: 100.64.0.0/10 + v6: fd7a:115c:a1e0::/48 + 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: 3h + disable_check_updates: false + node: + expire: 0 + ephemeral: + inactivity_timeout: 30m + database: + type: sqlite3 + sqlite: + path: /var/lib/headscale/db.sqlite + write_ahead_log: true + wal_autocheckpoint: 1000 + log: + level: info + format: text + dns: + magic_dns: false + override_local_dns: false + nameservers: + - 1.1.1.1 + - 1.0.0.1 + +headplane: + enabled: true + image: + tag: 0.7.0