diff --git a/deploy/helm/warp/templates/NOTES.txt b/deploy/helm/warp/templates/NOTES.txt index 4382d9f..6bbf1ee 100644 --- a/deploy/helm/warp/templates/NOTES.txt +++ b/deploy/helm/warp/templates/NOTES.txt @@ -15,3 +15,12 @@ Reach it locally: The signing key {{ if .Values.signingKey.existingSecret }}comes from the secret {{ .Values.signingKey.existingSecret }}{{ else }}is on the data volume, which is kept when the release is deleted{{ end }}. Losing it means other servers no longer recognise this one. +{{- if .Values.admin.enabled }} + +The admin panel is {{ if .Values.admin.ingress.enabled }}on https://{{ .Values.admin.ingress.host }}.{{ else }}reachable with: + + kubectl port-forward -n {{ .Release.Namespace }} svc/{{ include "warp.admin.fullname" . }} 8009:{{ .Values.admin.service.port }} + # then browse to http://localhost:8009{{ end }} + +Sign in with an account that has the admin flag. +{{- end }} diff --git a/deploy/helm/warp/templates/_helpers.tpl b/deploy/helm/warp/templates/_helpers.tpl index af35c15..8be9db1 100644 --- a/deploy/helm/warp/templates/_helpers.tpl +++ b/deploy/helm/warp/templates/_helpers.tpl @@ -44,3 +44,24 @@ file:/data/warp.db?_pragma=journal_mode(WAL)&_pragma=busy_timeout(10000)&_pragma /data/signing.key {{- end -}} {{- end -}} + +{{/* +The admin panel has its own name label, so the homeserver's Service, whose +selector is warp.selectorLabels, never picks up its pods. +*/}} +{{- define "warp.admin.fullname" -}} +{{- printf "%s-admin" (include "warp.fullname" . | trunc 57 | trimSuffix "-") -}} +{{- end -}} + +{{- define "warp.admin.labels" -}} +helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" }} +{{ include "warp.admin.selectorLabels" . }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{- define "warp.admin.selectorLabels" -}} +app.kubernetes.io/name: {{ printf "%s-admin" (include "warp.name" .) }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/component: admin +{{- end -}} diff --git a/deploy/helm/warp/templates/admin-deployment.yaml b/deploy/helm/warp/templates/admin-deployment.yaml new file mode 100644 index 0000000..e5e3ef0 --- /dev/null +++ b/deploy/helm/warp/templates/admin-deployment.yaml @@ -0,0 +1,77 @@ +{{- if .Values.admin.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "warp.admin.fullname" . }} + labels: + {{- include "warp.admin.labels" . | nindent 4 }} +spec: + # warp-admin keeps no state, so more replicas and rolling updates are fine. + replicas: {{ .Values.admin.replicas }} + selector: + matchLabels: + {{- include "warp.admin.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "warp.admin.selectorLabels" . | nindent 8 }} + {{- with .Values.admin.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.admin.podSecurityContext | nindent 8 }} + containers: + - name: warp-admin + image: "{{ .Values.admin.image.repository }}:{{ .Values.admin.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.admin.image.pullPolicy }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + ports: + - name: http + containerPort: 8009 + protocol: TCP + env: + - name: WARP_ADMIN_LISTEN + value: ":8009" + # The homeserver's own Service: warp-admin reaches /_warp/admin + # inside the cluster, so the public ingress can leave it out. + - name: WARP_ADMIN_HOMESERVER + value: {{ printf "http://%s:%v" (include "warp.fullname" .) .Values.service.port | quote }} + - name: WARP_ADMIN_LOG_FORMAT + value: json + {{- with .Values.admin.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + livenessProbe: + httpGet: + path: /healthz + port: http + periodSeconds: 30 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /healthz + port: http + periodSeconds: 10 + timeoutSeconds: 5 + resources: + {{- toYaml .Values.admin.resources | nindent 12 }} + {{- with .Values.admin.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.admin.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.admin.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/deploy/helm/warp/templates/admin-ingress.yaml b/deploy/helm/warp/templates/admin-ingress.yaml new file mode 100644 index 0000000..e668607 --- /dev/null +++ b/deploy/helm/warp/templates/admin-ingress.yaml @@ -0,0 +1,31 @@ +{{- if and .Values.admin.enabled .Values.admin.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "warp.admin.fullname" . }} + labels: + {{- include "warp.admin.labels" . | nindent 4 }} + {{- with .Values.admin.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.admin.ingress.className }} + ingressClassName: {{ . }} + {{- end }} + {{- with .Values.admin.ingress.tls }} + tls: + {{- toYaml . | nindent 4 }} + {{- end }} + rules: + - host: {{ required "admin.ingress.host is required" .Values.admin.ingress.host | quote }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ include "warp.admin.fullname" . }} + port: + name: http +{{- end }} diff --git a/deploy/helm/warp/templates/admin-service.yaml b/deploy/helm/warp/templates/admin-service.yaml new file mode 100644 index 0000000..364367d --- /dev/null +++ b/deploy/helm/warp/templates/admin-service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.admin.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "warp.admin.fullname" . }} + labels: + {{- include "warp.admin.labels" . | nindent 4 }} +spec: + type: {{ .Values.admin.service.type }} + ports: + - name: http + port: {{ .Values.admin.service.port }} + targetPort: http + protocol: TCP + selector: + {{- include "warp.admin.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/deploy/helm/warp/values.yaml b/deploy/helm/warp/values.yaml index 8842fa5..318f0e0 100644 --- a/deploy/helm/warp/values.yaml +++ b/deploy/helm/warp/values.yaml @@ -132,3 +132,61 @@ podAnnotations: {} nodeSelector: {} tolerations: [] affinity: {} + +# The admin panel: warp-admin, a separate service that serves the web UI and +# proxies only the login endpoints and /_warp/admin/v1 to the homeserver's +# Service. It is optional; the homeserver runs the same without it. +admin: + enabled: false + # warp-admin keeps no state, so it can run more than one replica. + replicas: 1 + + # There is no published image either: build it from the repository root + # with `make admin-docker` (or + # `docker build -t /warp-admin: -f apps/admin/Dockerfile .`) + # and push it. imagePullSecrets above applies to it too. + image: + repository: warp-admin + # Defaults to the chart's appVersion. + tag: "" + pullPolicy: IfNotPresent + + service: + type: ClusterIP + port: 8009 + + # Its own ingress, on a separate host: ideally one only admins can reach + # (an internal ingress class, an IP allow-list, or SSO in front). Without + # it, reach the panel with kubectl port-forward. Since warp-admin talks to + # the homeserver inside the cluster, the main ingress can then drop the + # /_warp path. + ingress: + enabled: false + className: "" + annotations: {} + host: admin.matrix.example.org + tls: [] + # - secretName: matrix-admin-tls + # hosts: [admin.matrix.example.org] + + # More WARP_ADMIN_* settings; see apps/admin/README.md. + extraEnv: [] + + resources: + requests: + cpu: 10m + memory: 32Mi + limits: + memory: 128Mi + + # The image runs as uid 10002. securityContext above applies to the + # container as well. + podSecurityContext: + runAsUser: 10002 + runAsGroup: 10002 + runAsNonRoot: true + + podAnnotations: {} + nodeSelector: {} + tolerations: [] + affinity: {} diff --git a/docs/deployment.md b/docs/deployment.md index be372c3..189336f 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -80,6 +80,22 @@ prometheus-operator `ServiceMonitor`. The ingress needs a read timeout above the 30-second long poll; `values.yaml` has the ingress-nginx annotations. +The admin panel is off by default. `admin.enabled=true` adds a Deployment +and Service for `warp-admin` on port 8009, pointed at the homeserver's +Service. Its image is built separately, also from the repository root: + +```sh +docker build -t registry.example.org/warp-admin:0.10.0 -f apps/admin/Dockerfile . +helm upgrade warp deploy/helm/warp --reuse-values --set admin.enabled=true --set admin.image.repository=registry.example.org/warp-admin --set admin.image.tag=0.10.0 +``` + +Without an ingress, reach it with `kubectl port-forward svc/warp-admin +8009`. `admin.ingress.enabled` with `admin.ingress.host` gives it its own +ingress on a separate host, ideally one only admins can reach. warp-admin +reaches `/_warp/admin` inside the cluster, so you can then drop `/_warp` +from `ingress.hosts[].paths`. `admin.resources` and `admin.replicas` are in +`values.yaml`. + ## Reverse proxy Behind a proxy that terminates TLS: