diff --git a/.github/tests/common.sh b/.github/tests/common.sh index 3211e38..7d5323b 100755 --- a/.github/tests/common.sh +++ b/.github/tests/common.sh @@ -55,6 +55,7 @@ print_spire_workload_status () { | Namespace | Workload | Status | | --------- | ---------------------------------------------- | ------ | | ${ns1} | ${release_name}-server |
$(k_rollout_status "${ns1}" statefulset "${release_name}-server")
| +| ${ns1} | ${release_name}-server |
$(k_rollout_status "${ns1}" deployments.apps "${release_name}-server")
| | ${ns2} | ${release_name}-spiffe-csi-driver |
$(k_rollout_status "${ns2}" daemonset "${release_name}-spiffe-csi-driver")
| | ${ns2} | ${release_name}-agent |
$(k_rollout_status "${ns2}" daemonset "${release_name}-agent")
| | ${ns1} | ${release_name}-spiffe-oidc-discovery-provider |
$(k_rollout_status "${ns1}" deployments.apps "${release_name}-spiffe-oidc-discovery-provider")
| diff --git a/charts/spire/README.md b/charts/spire/README.md index 570d01a..be050e8 100644 --- a/charts/spire/README.md +++ b/charts/spire/README.md @@ -244,11 +244,12 @@ Now you can interact with the Spire agent socket from your own application. The ### Spire server parameters -| Name | Description | Value | -| ---------------------------------------- | --------------------------------------------- | -------- | -| `spire-server.enabled` | Flag to enable Spire server | `true` | -| `spire-server.nameOverride` | Overrides the name of Spire server pods | `server` | -| `spire-server.controllerManager.enabled` | Enable controller manager and provision CRD's | `true` | +| Name | Description | Value | +| ---------------------------------------- | ------------------------------------------------------------------------- | ------------- | +| `spire-server.enabled` | Flag to enable Spire server | `true` | +| `spire-server.nameOverride` | Overrides the name of Spire server pods | `server` | +| `spire-server.kind` | Run spire server as deployment/statefulset. This feature is experimental. | `statefulset` | +| `spire-server.controllerManager.enabled` | Enable controller manager and provision CRD's | `true` | ### Spire agent parameters diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index 243f7b0..1ecad75 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -86,6 +86,7 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `image.repository` | The repository within the registry | `spiffe/spire-server` | | `image.pullPolicy` | The image pull policy | `IfNotPresent` | | `image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | +| `kind` | Define SPIRE server deployment type. Can be statefulset/deployment. Defaults to statefulset if not set. This feature is experimental. | `statefulset` | | `imagePullSecrets` | Pull secrets for images | `[]` | | `nameOverride` | Name override | `""` | | `crNameOverride` | Name override for any custom resources | `""` | diff --git a/charts/spire/charts/spire-server/templates/_helpers.tpl b/charts/spire/charts/spire-server/templates/_helpers.tpl index cd81b6a..87d8060 100644 --- a/charts/spire/charts/spire-server/templates/_helpers.tpl +++ b/charts/spire/charts/spire-server/templates/_helpers.tpl @@ -5,6 +5,17 @@ Expand the name of the chart. {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} +{{/* +Spire Server deployment/statefulset +*/}} +{{- define "spire-server.kind" -}} +{{- if not (has .Values.kind (list "statefulset" "deployment")) -}} + {{- fail "Unsupported deployment type" -}} +{{- else -}} + {{- .Values.kind -}} +{{- end -}} +{{- end }} + {{/* Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). diff --git a/charts/spire/charts/spire-server/templates/hpa.yaml b/charts/spire/charts/spire-server/templates/hpa.yaml index 4c1c41c..65b195e 100644 --- a/charts/spire/charts/spire-server/templates/hpa.yaml +++ b/charts/spire/charts/spire-server/templates/hpa.yaml @@ -9,7 +9,11 @@ metadata: spec: scaleTargetRef: apiVersion: apps/v1 + {{- if eq .Values.kind "statefulset" }} kind: StatefulSet + {{- else if eq .Values.kind "deployment" }} + kind: Deployment + {{- end }} name: {{ include "spire-server.fullname" . }} minReplicas: {{ .Values.autoscaling.minReplicas }} {{- if and (eq .Values.dataStore.sql.databaseType "sqlite3") .Values.autoscaling.enabled (gt (int .Values.autoscaling.maxReplicas) 1) }} diff --git a/charts/spire/charts/spire-server/templates/pre-delete-hook.yaml b/charts/spire/charts/spire-server/templates/pre-delete-hook.yaml index 86226a1..2a14be9 100644 --- a/charts/spire/charts/spire-server/templates/pre-delete-hook.yaml +++ b/charts/spire/charts/spire-server/templates/pre-delete-hook.yaml @@ -21,7 +21,7 @@ metadata: "helm.sh/hook-delete-policy": before-hook-creation, hook-succeeded, hook-failed rules: - apiGroups: ["apps"] - resources: ["statefulsets"] + resources: ["{{ include "spire-server.kind" . }}s"] resourceNames: [{{ include "spire-server.fullname" . | quote }}] verbs: ["get", "delete"] - apiGroups: [""] @@ -73,7 +73,7 @@ spec: - delete - -n - {{ include "spire-server.namespace" . }} - - statefulset + - {{ include "spire-server.kind" . }} - {{ include "spire-server.fullname" . }} - --wait - name: pre-delete-check diff --git a/charts/spire/charts/spire-server/templates/statefulset.yaml b/charts/spire/charts/spire-server/templates/server-resource.yaml similarity index 95% rename from charts/spire/charts/spire-server/templates/statefulset.yaml rename to charts/spire/charts/spire-server/templates/server-resource.yaml index 2d3f48a..8a0634b 100644 --- a/charts/spire/charts/spire-server/templates/statefulset.yaml +++ b/charts/spire/charts/spire-server/templates/server-resource.yaml @@ -3,11 +3,30 @@ {{- $configSum3 := (include (print $.Template.BasePath "/controller-manager-configmap.yaml") . | sha256sum) }} {{- $configSumTornjak := (include (print $.Template.BasePath "/tornjak-config.yaml") . | sha256sum) }} {{- $fullname := include "spire-server.fullname" . }} -{{- if not (has .Values.persistence.type (list "pvc" "hostPath" "emptyDir")) }} -{{- fail "persistence.type must be one of [\"pvc\", \"hostPath\", \"emptyDir\"]" }} + +{{- if eq .Values.kind "statefulset" }} +{{- if not (has .Values.persistence.type (list "pvc" "hostPath" "emptyDir")) }} +{{- fail "persistence.type must be one of [\"pvc\", \"hostPath\", \"emptyDir\"]" }} +{{- end }} +{{- else if eq .Values.kind "deployment" }} +{{- if (has .Values.persistence.type (list "pvc" "hostPath")) }} +{{- fail "When running as deployment, persistence can't be set. 'persistence.type' must be [\"emptyDir\"]" }} +{{- end }} +{{- if (eq .Values.dataStore.sql.databaseType "sqlite3") }} +{{- fail "When running as deployment, sqlite3 can't be used." }} +{{- end }} +{{- if (eq (.Values.keyManager.disk.enabled | toString) "true") }} +{{- fail "When running as deployment, disk keymanager can't be used. 'keyManager.disk.enabled' must be false." }} +{{- end }} +{{- else }} +{{- fail "Unsupported kind." }} {{- end }} apiVersion: apps/v1 +{{- if eq .Values.kind "statefulset" }} kind: StatefulSet +{{- else if eq .Values.kind "deployment" }} +kind: Deployment +{{- end }} metadata: name: {{ include "spire-server.fullname" . }} namespace: {{ include "spire-server.namespace" . }} @@ -21,7 +40,9 @@ spec: {{- end }} replicas: {{ .Values.replicaCount }} {{- end }} + {{- if eq .Values.kind "statefulset" }} serviceName: {{ include "spire-server.fullname" . }} + {{- end }} selector: matchLabels: {{- include "spire-server.selectorLabels" . | nindent 6 }} @@ -491,4 +512,4 @@ spec: {{- if .Values.persistence.storageClass }} storageClassName: {{ .Values.persistence.storageClass }} {{- end }} - {{- 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 501cda8..5c449be 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -20,6 +20,9 @@ image: pullPolicy: IfNotPresent tag: "" +## @param kind Define SPIRE server deployment type. Can be statefulset/deployment. Defaults to statefulset if not set. This feature is experimental. +kind: statefulset + ## @param imagePullSecrets [array] Pull secrets for images imagePullSecrets: [] diff --git a/charts/spire/values.yaml b/charts/spire/values.yaml index e5de054..81558a3 100644 --- a/charts/spire/values.yaml +++ b/charts/spire/values.yaml @@ -108,6 +108,8 @@ spire-server: enabled: true ## @param spire-server.nameOverride Overrides the name of Spire server pods nameOverride: server + ## @param spire-server.kind Run spire server as deployment/statefulset. This feature is experimental. + kind: statefulset controllerManager: ## @param spire-server.controllerManager.enabled Enable controller manager and provision CRD's enabled: true diff --git a/examples/stateless-server/README.md b/examples/stateless-server/README.md new file mode 100644 index 0000000..4475fae --- /dev/null +++ b/examples/stateless-server/README.md @@ -0,0 +1,23 @@ +# Example stateless server + +To install Spire Server as a deployment(stateless), you need to use an external database. This runs spire-server as stateless microservice enabling HA. + +### WARNING +The following configurations are not supported for running spire-server as deployment. +1. spire-server.persistence.type +2. spire-server.dataStore.sql.databaseType: "sqlite3" +3. spire-server.keyManager.disk +4. spire-server.tornjak + +If manually deploying for testing, you can create an incluster or use an external database and put the database password into an environment variable. + +Next, edit your-values.yaml with your settings as described in the [production install instructions](https://artifacthub.io/packages/helm/spiffe/spire#production). Check it into your git repo if using one. + +Then, deploy the chart pointing at your mysql instance like so: + +```shell +helm upgrade --install --namespace spire-mgmt spire spire --repo https://spiffe.github.io/helm-charts-hardened/ -f examples/stateless-server/values.yaml --set "spire-server.dataStore.sql.password=${DBPW}" -f your-values.yaml +``` + +See the [production install instructions](https://artifacthub.io/packages/helm/spiffe/spire#production) for production recommendations. +See [values.yaml](./values.yaml) for more details on the chart configurations to achieve this setup. diff --git a/examples/stateless-server/run-tests.sh b/examples/stateless-server/run-tests.sh new file mode 100755 index 0000000..6f3bd7a --- /dev/null +++ b/examples/stateless-server/run-tests.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +set -xe + +SCRIPT="$(readlink -f "$0")" +SCRIPTPATH="$(dirname "${SCRIPT}")" +TESTDIR="${SCRIPTPATH}/../../.github/tests" +DEPS="${TESTDIR}/dependencies" + +# shellcheck source=/dev/null +source "${SCRIPTPATH}/../../.github/scripts/parse-versions.sh" +# shellcheck source=/dev/null +source "${TESTDIR}/common.sh" + +CLEANUP=1 + +for i in "$@"; do + case $i in + -c) + CLEANUP=0 + shift # past argument=value + ;; + esac +done + +teardown() { + print_helm_releases + print_spire_workload_status spire-server + print_spire_workload_status spire-system + + if [[ "$1" -ne 0 ]]; then + get_namespace_details spire-server + get_namespace_details spire-system + fi + + if [ "${CLEANUP}" -eq 1 ]; then + helm uninstall --namespace "spire-server" spire 2>/dev/null || true + helm uninstall --namespace mysql mysql 2>/dev/null || true + kubectl delete ns spire-server 2>/dev/null || true + kubectl delete ns spire-system 2>/dev/null || true + fi +} + +trap 'EC=$? && trap - SIGTERM && teardown $EC' SIGINT SIGTERM EXIT + +kubectl create namespace spire-system --dry-run=client -o yaml | kubectl apply -f - +kubectl label namespace spire-system pod-security.kubernetes.io/enforce=privileged || true +kubectl create namespace spire-server --dry-run=client -o yaml | kubectl apply -f - +kubectl label namespace spire-server pod-security.kubernetes.io/enforce=restricted || true + +helm upgrade --install mysql mysql --version "$VERSION_MYSQL" --repo "$HELM_REPO_MYSQL" \ + --namespace spire-server \ + --values "${DEPS}/mysql.yaml" \ + --wait + +helm upgrade --install --namespace "spire-server" \ + --values "${COMMON_TEST_YOUR_VALUES},${SCRIPTPATH}/values.yaml,${SCRIPTPATH}/../misc/values-node-pod-antiaffinity.yaml" \ + --set 'spire-server.dataStore.sql.password=sp1ff3Test' --wait spire charts/spire +helm test --namespace "spire-server" spire diff --git a/examples/stateless-server/values.yaml b/examples/stateless-server/values.yaml new file mode 100644 index 0000000..f9f2753 --- /dev/null +++ b/examples/stateless-server/values.yaml @@ -0,0 +1,18 @@ +spire-server: + deploymentType: deployment + replicaCount: 2 + persistence: + type: emptyDir + keyManager: + disk: + enabled: false + memory: + enabled: true + dataStore: + sql: + databaseType: mysql + databaseName: spire-server + host: mysql + port: 3306 + username: spire + # password: CHANGE_ME