From 05d0f4778d0be7eed7223eb76b32d9d33cbadef9 Mon Sep 17 00:00:00 2001 From: Mariusz Sabath Date: Wed, 22 Mar 2023 13:09:09 -0400 Subject: [PATCH] Introduction of Tornjak to SPIRE Server helm charts (#144) This PR introduces a simplified version of [Tornjak](https://github.com/spiffe/tornjak) to support UI and SPIRE control plane. It extends the `/charts/spire-server` sub-chart by injecting *tornjak* container to the *spire-server* pod. Tornjak image consists of Tornjak API (Tornjak Backend), Tornjak UI (Frontend), and database for storing Tornjak specific information. Tornjak Backend communicates with SPIRE API via SPIRE Server socket. Since Tornjak UI (Frontend) is a React code that renders in a browser, it needs to communicate with Tornjak APIs, and that requires communication ports to be open, either via Ingress (in Cloud deployment) or via port forwarding in local deployments (e.g. kind, minikube etc). Typically port 10000 is used for HTTP connection to Backend, and port 3000 for the HTTP connection to Frontend. End user management, TLS, and mTLS connections will be addressed by future PRs. This PR resolves issue #31 --------- Signed-off-by: Mariusz Sabath Signed-off-by: Marco Franssen Co-authored-by: Kevin Fox Co-authored-by: Marco Franssen Co-authored-by: Pete Cable Co-authored-by: Dennis Gove --- charts/spire/charts/spire-server/README.md | 8 +++ .../charts/spire-server/templates/NOTES.txt | 18 ++++++ .../spire-server/templates/_helpers.tpl | 33 ++++++++++- .../spire-server/templates/service.yaml | 33 +++++++++++ .../spire-server/templates/statefulset.yaml | 55 +++++++++++++++++++ .../tests/test-tornjak-connection.yaml | 27 +++++++++ .../templates/tornjak-config.yaml | 23 ++++++++ charts/spire/charts/spire-server/values.yaml | 23 ++++++++ 8 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 charts/spire/charts/spire-server/templates/tests/test-tornjak-connection.yaml create mode 100644 charts/spire/charts/spire-server/templates/tornjak-config.yaml diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index ac897b7..09e781f 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -89,6 +89,14 @@ A Helm chart to install the SPIRE server. | telemetry.prometheus.enabled | bool | `false` | | | tolerations | list | `[]` | | | topologySpreadConstraints | list | `[]` | | +| tornjak.config.backend.dataStore.driver | string | `"sqlite3"` | | +| tornjak.config.backend.dataStore.file | string | `"/run/spire/data/tornjak.sqlite3"` | | +| tornjak.config.frontend.apiServerURL | string | `"http://localhost:10000"` | | +| tornjak.enabled | bool | `true` | | +| tornjak.image.pullPolicy | string | `"IfNotPresent"` | | +| tornjak.image.registry | string | `"ghcr.io"` | | +| tornjak.image.repository | string | `"spiffe/tornjak"` | | +| tornjak.image.version | string | `"latest"` | | | trustDomain | string | `"example.org"` | | | upstreamAuthority.certManager.enabled | bool | `false` | | | upstreamAuthority.certManager.issuer_group | string | `"cert-manager.io"` | | diff --git a/charts/spire/charts/spire-server/templates/NOTES.txt b/charts/spire/charts/spire-server/templates/NOTES.txt index b013798..27b3c78 100644 --- a/charts/spire/charts/spire-server/templates/NOTES.txt +++ b/charts/spire/charts/spire-server/templates/NOTES.txt @@ -4,3 +4,21 @@ Installed {{ .Chart.Name }}… kubectl exec -n {{ .Release.Namespace }} {{ include "spire-server.fullname" . }}-0 -c spire-server -- \ spire-server entry show + +{{- if eq (.Values.tornjak.enabled | toString) "true" }} + +### WARNING ### +This Tornjak is configured without authentication and it is intended for +testing only. Please do not use this version in production. + +Tornjak APIs (Backend): + kubectl -n {{ include "spire-server.namespace" . }} port-forward {{ include "spire-server.fullname" . }}-0 10000:10000 + +Tornjak UI (Frontend): + kubectl -n {{ include "spire-server.namespace" . }} port-forward {{ include "spire-server.fullname" . }}-0 3000:3000 + +Tornjak API access: {{ include "tornjak.apiURL" . }} +Tornjak UI access: {{ include "tornjak.FrontendURL" . }} + +Installed {{ include "spire-tornjak.fullname" . }}… +{{- end }} diff --git a/charts/spire/charts/spire-server/templates/_helpers.tpl b/charts/spire/charts/spire-server/templates/_helpers.tpl index 7e7c45c..0060b36 100644 --- a/charts/spire/charts/spire-server/templates/_helpers.tpl +++ b/charts/spire/charts/spire-server/templates/_helpers.tpl @@ -84,7 +84,6 @@ Create the name of the service account to use {{- end -}} {{- end }} - {{- define "spire-server.upstream-ca-secret" -}} {{- $root := . }} {{- with .Values.upstreamAuthority.disk -}} @@ -111,3 +110,35 @@ Create the name of the service account to use [{{ printf "%s:%s-agent" .Release.Namespace .Release.Name | quote }}] {{- end }} {{- end }} + +{{/* +Tornjak specific section +*/}} + +{{- define "spire-tornjak.fullname" -}} +{{ include "spire-server.fullname" . | trimSuffix "-server" }}-tornjak +{{- end }} +{{- define "spire-tornjak.config" -}} +{{ include "spire-tornjak.fullname" . }}-config +{{- end }} +{{- define "spire-tornjak.frontend" -}} +{{ include "spire-tornjak.fullname" . }}-fe +{{- end }} +{{- define "spire-tornjak.backend" -}} +{{ include "spire-tornjak.fullname" . }}-be +{{- end }} + +{{/* +Create URL for accessing Tornjak Backend +*/}} +{{- define "tornjak.apiURL" -}} +{{- default .Values.tornjak.config.frontend.apiServerURL }} +{{- end }} + +{{/* +Create URL for accessing Tornjak Frontend +*/}} +{{- define "tornjak.FrontendURL" -}} +{{- $feurl := print "http://localhost:3000" }} +{{- $feurl }} +{{- end }} \ No newline at end of file diff --git a/charts/spire/charts/spire-server/templates/service.yaml b/charts/spire/charts/spire-server/templates/service.yaml index 5cdaf50..673fae1 100644 --- a/charts/spire/charts/spire-server/templates/service.yaml +++ b/charts/spire/charts/spire-server/templates/service.yaml @@ -26,3 +26,36 @@ spec: {{- end }} selector: {{- include "spire-server.selectorLabels" . | nindent 4 }} + +{{- if eq (.Values.tornjak.enabled | toString) "true" }} +--- +apiVersion: v1 +kind: Service +metadata: + namespace: {{ include "spire-server.namespace" . }} + name: {{ include "spire-tornjak.frontend" . }} +spec: + type: {{ .Values.service.type }} # ClusterIP + selector: + {{- include "spire-server.selectorLabels" . | nindent 4 }} + ports: + - name: {{ include "spire-tornjak.frontend" . }} + port: 3000 + targetPort: 3000 + protocol: TCP +--- +apiVersion: v1 +kind: Service +metadata: + namespace: {{ include "spire-server.namespace" . }} + name: {{ include "spire-tornjak.backend" . }} +spec: + type: {{ .Values.service.type }} # ClusterIP + selector: + {{- include "spire-server.selectorLabels" . | nindent 4 }} + ports: + - name: {{ include "spire-tornjak.backend" . }} + port: 10000 + targetPort: 10000 + protocol: TCP +{{- end }} \ No newline at end of file diff --git a/charts/spire/charts/spire-server/templates/statefulset.yaml b/charts/spire/charts/spire-server/templates/statefulset.yaml index 2773bf6..e7b4f36 100644 --- a/charts/spire/charts/spire-server/templates/statefulset.yaml +++ b/charts/spire/charts/spire-server/templates/statefulset.yaml @@ -1,5 +1,6 @@ {{- $configSum := (include (print $.Template.BasePath "/configmap.yaml") . | sha256sum) }} {{- $configSum2 := (include (print $.Template.BasePath "/controller-manager-configmap.yaml") . | sha256sum) }} +{{- $configSumTornjak := (include (print $.Template.BasePath "/tornjak-config.yaml") . | sha256sum) }} {{- $fullname := include "spire-server.fullname" . }} apiVersion: apps/v1 kind: StatefulSet @@ -21,6 +22,7 @@ spec: annotations: checksum/config: {{ $configSum }} checksum/config2: {{ $configSum2 }} + checksum/configTornjak: {{ $configSumTornjak }} {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} @@ -144,6 +146,51 @@ spec: mountPath: /tmp readOnly: false {{- end }} + + {{- if eq (.Values.tornjak.enabled | toString) "true" }} + - name: tornjak + securityContext: + {{- toYaml .Values.controllerManager.securityContext | nindent 12 }} + image: {{ template "spire-server.image" (dict "appVersion" $.Chart.AppVersion "image" .Values.tornjak.image) }} + imagePullPolicy: {{ .Values.tornjak.image.pullPolicy }} + startupProbe: + httpGet: + scheme: HTTP + port: 3000 + failureThreshold: 6 + initialDelaySeconds: 60 + periodSeconds: 30 + successThreshold: 1 + timeoutSeconds: 10 + env: + {{- if .Values.tornjak.config.frontend }} + - name: REACT_APP_API_SERVER_URI + value: {{ include "tornjak.apiURL" . | required "Either .Values.tornjak.config.backend.ingress or .Values.tornjak.config.frontend.apiServerURL is required." }} + {{- end }} + args: + - -c + - /run/spire/config/server.conf + - -t + - /run/spire/tornjak-config/server.conf + ports: + - containerPort: 3000 + protocol: TCP + volumeMounts: + - name: {{ include "spire-tornjak.config" . }} + mountPath: /run/spire/tornjak-config + - name: spire-server-socket + mountPath: /tmp/spire-server/private + readOnly: true + - name: spire-config + mountPath: /run/spire/config + readOnly: true + {{- if eq (.Values.dataStorage.enabled | toString) "true" }} + - name: spire-data + mountPath: /run/spire/data + readOnly: false + {{- end }} + {{- end }} + {{- if gt (len .Values.extraContainers) 0 }} {{- toYaml .Values.extraContainers | nindent 8 }} {{- end }} @@ -181,6 +228,14 @@ spec: configMap: name: {{ include "spire-controller-manager.fullname" . }} {{- end }} + {{- if eq (.Values.tornjak.enabled | toString) "true" }} + {{- if .Values.tornjak.config }} + - name: {{ include "spire-tornjak.config" . }} + configMap: + defaultMode: 420 + name: {{ include "spire-tornjak.config" . }} + {{- end }} + {{- end }} {{- if gt (len .Values.extraVolumes) 0 }} {{- toYaml .Values.extraVolumes | nindent 8 }} {{- end }} diff --git a/charts/spire/charts/spire-server/templates/tests/test-tornjak-connection.yaml b/charts/spire/charts/spire-server/templates/tests/test-tornjak-connection.yaml new file mode 100644 index 0000000..9dd5ca7 --- /dev/null +++ b/charts/spire/charts/spire-server/templates/tests/test-tornjak-connection.yaml @@ -0,0 +1,27 @@ +{{- if eq (.Values.tornjak.enabled | toString) "true" }} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "spire-tornjak.fullname" . }}-test-connection" + namespace: {{ include "spire-server.namespace" . }} + labels: + annotations: + "helm.sh/hook": test +spec: + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 4 }} + containers: + - name: wget-tornjak-backend + image: busybox + command: ['wget'] + args: ['--no-check-certificate', '-O', '/dev/null', 'http://{{ include "spire-tornjak.backend" . }}:10000/api/tornjak/serverinfo'] + securityContext: + {{- toYaml .Values.securityContext | nindent 8 }} + - name: wget-tornjak-frontend + image: busybox + command: ['wget'] + args: ['--no-check-certificate', '-O', '/dev/null', 'http://{{ include "spire-tornjak.frontend" . }}:3000'] + securityContext: + {{- toYaml .Values.securityContext | nindent 8 }} + restartPolicy: Never +{{- end }} \ No newline at end of file diff --git a/charts/spire/charts/spire-server/templates/tornjak-config.yaml b/charts/spire/charts/spire-server/templates/tornjak-config.yaml new file mode 100644 index 0000000..407c23e --- /dev/null +++ b/charts/spire/charts/spire-server/templates/tornjak-config.yaml @@ -0,0 +1,23 @@ +{{- if eq (.Values.tornjak.enabled | toString) "true" }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "spire-tornjak.config" . }} + namespace: {{ include "spire-server.namespace" . }} +data: + server.conf: | + server { + metadata = "insert metadata" + } + + plugins { + {{- if .Values.tornjak.config.backend.dataStore }} + DataStore "sql" { + plugin_data { + drivername = "{{ .Values.tornjak.config.backend.dataStore.driver }}" + filename = "{{ .Values.tornjak.config.backend.dataStore.file }}" + } + } + {{- end }} + } + {{- end }} \ No newline at end of file diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index 5324b55..c3a9c35 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -203,3 +203,26 @@ nodeAttestor: k8sPsat: enabled: true serviceAccountAllowList: [] + +# tornjak - Tornjak specific configuration +tornjak: + enabled: true + # image - Tornjak image (frontend + backend) if not separated above + image: # ghcr.io/spiffe/tornjak + registry: ghcr.io + repository: spiffe/tornjak + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + # TODO we should use a specific Tornjak version instead of 'latest' + version: "latest" + config: + # Front-end specific configuration: + frontend: + # apiServerURL - URL of the Tornjak back-end + apiServerURL: "http://localhost:10000" # 👈 Use it for minikube or kind + # Back-end specific configuration + backend: + # dataStore - persistent DB for storing Tornjak specific information + dataStore: + driver: "sqlite3" + file: "/run/spire/data/tornjak.sqlite3" \ No newline at end of file