From 89f4b0dbdfa1b40f0a0fc04da4b67fa09835574c Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 14 Jan 2026 13:40:29 -0300 Subject: [PATCH] vault: stabilize injector templates and add health apps --- .../applications/health/kustomization.yaml | 33 ++++ .../applications/kustomization.yaml | 1 + .../bstein-dev-home/backend-deployment.yaml | 16 +- .../chat-ai-gateway-deployment.yaml | 16 +- .../portal-onboarding-e2e-test-job.yaml | 16 +- .../vaultwarden-cred-sync-cronjob.yaml | 16 +- services/gitea/deployment.yaml | 24 +-- services/health/endurain-data-pvc.yaml | 12 ++ services/health/endurain-deployment.yaml | 147 +++++++++++++++ services/health/endurain-ingress.yaml | 26 +++ .../health/endurain-oidc-config-cronjob.yaml | 87 +++++++++ services/health/endurain-service.yaml | 15 ++ services/health/kustomization.yaml | 30 ++++ services/health/namespace.yaml | 5 + .../health/scripts/endurain_oidc_configure.sh | 134 ++++++++++++++ .../scripts/sparkyfitness_oidc_configure.sh | 134 ++++++++++++++ services/health/secretproviderclass.yaml | 167 +++++++++++++++++ services/health/serviceaccount.yaml | 6 + services/health/sparkyfitness-data-pvc.yaml | 12 ++ .../sparkyfitness-frontend-deployment.yaml | 81 +++++++++ .../sparkyfitness-frontend-service.yaml | 15 ++ services/health/sparkyfitness-ingress.yaml | 26 +++ .../sparkyfitness-oidc-config-cronjob.yaml | 89 +++++++++ .../sparkyfitness-server-deployment.yaml | 170 ++++++++++++++++++ .../health/sparkyfitness-server-service.yaml | 15 ++ services/health/vault-sync-deployment.yaml | 34 ++++ services/keycloak/deployment.yaml | 51 ++---- .../endurain-oidc-secret-ensure-job.yaml | 52 ++++++ .../harbor-oidc-secret-ensure-job.yaml | 4 +- services/keycloak/kustomization.yaml | 8 + services/keycloak/ldap-federation-job.yaml | 20 +-- .../keycloak/logs-oidc-secret-ensure-job.yaml | 4 +- services/keycloak/mas-secrets-ensure-job.yaml | 4 +- services/keycloak/portal-e2e-client-job.yaml | 20 +-- ...al-e2e-execute-actions-email-test-job.yaml | 20 +-- .../portal-e2e-target-client-job.yaml | 20 +-- ...al-e2e-token-exchange-permissions-job.yaml | 20 +-- .../portal-e2e-token-exchange-test-job.yaml | 20 +-- services/keycloak/realm-settings-job.yaml | 20 +-- .../scripts/endurain_oidc_secret_ensure.sh | 87 +++++++++ .../sparkyfitness_oidc_secret_ensure.sh | 87 +++++++++ .../sparkyfitness-oidc-secret-ensure-job.yaml | 52 ++++++ .../synapse-oidc-secret-ensure-job.yaml | 4 +- services/keycloak/user-overrides-job.yaml | 20 +-- .../vault-oidc-secret-ensure-job.yaml | 4 +- services/nextcloud-mail-sync/cronjob.yaml | 20 +-- services/nextcloud/deployment.yaml | 20 +-- services/nextcloud/maintenance-cronjob.yaml | 20 +-- services/outline/deployment.yaml | 20 +-- services/planka/deployment.yaml | 20 +-- .../vault/scripts/vault_k8s_auth_configure.sh | 4 +- services/vaultwarden/deployment.yaml | 12 +- 52 files changed, 1736 insertions(+), 224 deletions(-) create mode 100644 clusters/atlas/flux-system/applications/health/kustomization.yaml create mode 100644 services/health/endurain-data-pvc.yaml create mode 100644 services/health/endurain-deployment.yaml create mode 100644 services/health/endurain-ingress.yaml create mode 100644 services/health/endurain-oidc-config-cronjob.yaml create mode 100644 services/health/endurain-service.yaml create mode 100644 services/health/kustomization.yaml create mode 100644 services/health/namespace.yaml create mode 100644 services/health/scripts/endurain_oidc_configure.sh create mode 100644 services/health/scripts/sparkyfitness_oidc_configure.sh create mode 100644 services/health/secretproviderclass.yaml create mode 100644 services/health/serviceaccount.yaml create mode 100644 services/health/sparkyfitness-data-pvc.yaml create mode 100644 services/health/sparkyfitness-frontend-deployment.yaml create mode 100644 services/health/sparkyfitness-frontend-service.yaml create mode 100644 services/health/sparkyfitness-ingress.yaml create mode 100644 services/health/sparkyfitness-oidc-config-cronjob.yaml create mode 100644 services/health/sparkyfitness-server-deployment.yaml create mode 100644 services/health/sparkyfitness-server-service.yaml create mode 100644 services/health/vault-sync-deployment.yaml create mode 100644 services/keycloak/endurain-oidc-secret-ensure-job.yaml create mode 100644 services/keycloak/scripts/endurain_oidc_secret_ensure.sh create mode 100644 services/keycloak/scripts/sparkyfitness_oidc_secret_ensure.sh create mode 100644 services/keycloak/sparkyfitness-oidc-secret-ensure-job.yaml diff --git a/clusters/atlas/flux-system/applications/health/kustomization.yaml b/clusters/atlas/flux-system/applications/health/kustomization.yaml new file mode 100644 index 0000000..f666d39 --- /dev/null +++ b/clusters/atlas/flux-system/applications/health/kustomization.yaml @@ -0,0 +1,33 @@ +# clusters/atlas/flux-system/applications/health/kustomization.yaml +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: health + namespace: flux-system +spec: + interval: 10m + path: ./services/health + prune: true + sourceRef: + kind: GitRepository + name: flux-system + targetNamespace: health + dependsOn: + - name: keycloak + - name: postgres + - name: traefik + - name: vault + healthChecks: + - apiVersion: apps/v1 + kind: Deployment + name: endurain + namespace: health + - apiVersion: apps/v1 + kind: Deployment + name: sparkyfitness-server + namespace: health + - apiVersion: apps/v1 + kind: Deployment + name: sparkyfitness-frontend + namespace: health + wait: false diff --git a/clusters/atlas/flux-system/applications/kustomization.yaml b/clusters/atlas/flux-system/applications/kustomization.yaml index d48cf9e..cc32c85 100644 --- a/clusters/atlas/flux-system/applications/kustomization.yaml +++ b/clusters/atlas/flux-system/applications/kustomization.yaml @@ -27,3 +27,4 @@ resources: - nextcloud-mail-sync/kustomization.yaml - outline/kustomization.yaml - planka/kustomization.yaml + - health/kustomization.yaml diff --git a/services/bstein-dev-home/backend-deployment.yaml b/services/bstein-dev-home/backend-deployment.yaml index 659cd33..e18a372 100644 --- a/services/bstein-dev-home/backend-deployment.yaml +++ b/services/bstein-dev-home/backend-deployment.yaml @@ -19,20 +19,20 @@ spec: vault.hashicorp.com/role: "bstein-dev-home" vault.hashicorp.com/agent-inject-secret-portal-env.sh: "kv/data/atlas/portal/atlas-portal-db" vault.hashicorp.com/agent-inject-template-portal-env.sh: | - {{- with secret "kv/data/atlas/portal/atlas-portal-db" -}} + {{ with secret "kv/data/atlas/portal/atlas-portal-db" }} export PORTAL_DATABASE_URL="{{ .Data.data.PORTAL_DATABASE_URL }}" - {{- end }} - {{- with secret "kv/data/atlas/portal/bstein-dev-home-keycloak-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/portal/bstein-dev-home-keycloak-admin" }} export KEYCLOAK_ADMIN_CLIENT_SECRET="{{ .Data.data.client_secret }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/chat-ai-keys-runtime" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/chat-ai-keys-runtime" }} export CHAT_KEY_MATRIX="{{ .Data.data.matrix }}" export CHAT_KEY_HOMEPAGE="{{ .Data.data.homepage }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/portal-e2e-client" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/portal-e2e-client" }} export PORTAL_E2E_CLIENT_ID="{{ .Data.data.client_id }}" export PORTAL_E2E_CLIENT_SECRET="{{ .Data.data.client_secret }}" - {{- end }} + {{ end }} spec: automountServiceAccountToken: true serviceAccountName: bstein-dev-home diff --git a/services/bstein-dev-home/chat-ai-gateway-deployment.yaml b/services/bstein-dev-home/chat-ai-gateway-deployment.yaml index fba58bc..40d74fe 100644 --- a/services/bstein-dev-home/chat-ai-gateway-deployment.yaml +++ b/services/bstein-dev-home/chat-ai-gateway-deployment.yaml @@ -19,20 +19,20 @@ spec: vault.hashicorp.com/role: "bstein-dev-home" vault.hashicorp.com/agent-inject-secret-portal-env.sh: "kv/data/atlas/portal/atlas-portal-db" vault.hashicorp.com/agent-inject-template-portal-env.sh: | - {{- with secret "kv/data/atlas/portal/atlas-portal-db" -}} + {{ with secret "kv/data/atlas/portal/atlas-portal-db" }} export PORTAL_DATABASE_URL="{{ .Data.data.PORTAL_DATABASE_URL }}" - {{- end }} - {{- with secret "kv/data/atlas/portal/bstein-dev-home-keycloak-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/portal/bstein-dev-home-keycloak-admin" }} export KEYCLOAK_ADMIN_CLIENT_SECRET="{{ .Data.data.client_secret }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/chat-ai-keys-runtime" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/chat-ai-keys-runtime" }} export CHAT_KEY_MATRIX="{{ .Data.data.matrix }}" export CHAT_KEY_HOMEPAGE="{{ .Data.data.homepage }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/portal-e2e-client" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/portal-e2e-client" }} export PORTAL_E2E_CLIENT_ID="{{ .Data.data.client_id }}" export PORTAL_E2E_CLIENT_SECRET="{{ .Data.data.client_secret }}" - {{- end }} + {{ end }} spec: serviceAccountName: bstein-dev-home nodeSelector: diff --git a/services/bstein-dev-home/portal-onboarding-e2e-test-job.yaml b/services/bstein-dev-home/portal-onboarding-e2e-test-job.yaml index 1d926b2..1f725f6 100644 --- a/services/bstein-dev-home/portal-onboarding-e2e-test-job.yaml +++ b/services/bstein-dev-home/portal-onboarding-e2e-test-job.yaml @@ -13,20 +13,20 @@ spec: vault.hashicorp.com/role: "bstein-dev-home" vault.hashicorp.com/agent-inject-secret-portal-env.sh: "kv/data/atlas/portal/atlas-portal-db" vault.hashicorp.com/agent-inject-template-portal-env.sh: | - {{- with secret "kv/data/atlas/portal/atlas-portal-db" -}} + {{ with secret "kv/data/atlas/portal/atlas-portal-db" }} export PORTAL_DATABASE_URL="{{ .Data.data.PORTAL_DATABASE_URL }}" - {{- end }} - {{- with secret "kv/data/atlas/portal/bstein-dev-home-keycloak-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/portal/bstein-dev-home-keycloak-admin" }} export KEYCLOAK_ADMIN_CLIENT_SECRET="{{ .Data.data.client_secret }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/chat-ai-keys-runtime" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/chat-ai-keys-runtime" }} export CHAT_KEY_MATRIX="{{ .Data.data.matrix }}" export CHAT_KEY_HOMEPAGE="{{ .Data.data.homepage }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/portal-e2e-client" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/portal-e2e-client" }} export PORTAL_E2E_CLIENT_ID="{{ .Data.data.client_id }}" export PORTAL_E2E_CLIENT_SECRET="{{ .Data.data.client_secret }}" - {{- end }} + {{ end }} spec: restartPolicy: Never serviceAccountName: bstein-dev-home diff --git a/services/bstein-dev-home/vaultwarden-cred-sync-cronjob.yaml b/services/bstein-dev-home/vaultwarden-cred-sync-cronjob.yaml index b46a2e3..efbab7e 100644 --- a/services/bstein-dev-home/vaultwarden-cred-sync-cronjob.yaml +++ b/services/bstein-dev-home/vaultwarden-cred-sync-cronjob.yaml @@ -19,20 +19,20 @@ spec: vault.hashicorp.com/role: "bstein-dev-home" vault.hashicorp.com/agent-inject-secret-portal-env.sh: "kv/data/atlas/portal/atlas-portal-db" vault.hashicorp.com/agent-inject-template-portal-env.sh: | - {{- with secret "kv/data/atlas/portal/atlas-portal-db" -}} + {{ with secret "kv/data/atlas/portal/atlas-portal-db" }} export PORTAL_DATABASE_URL="{{ .Data.data.PORTAL_DATABASE_URL }}" - {{- end }} - {{- with secret "kv/data/atlas/portal/bstein-dev-home-keycloak-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/portal/bstein-dev-home-keycloak-admin" }} export KEYCLOAK_ADMIN_CLIENT_SECRET="{{ .Data.data.client_secret }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/chat-ai-keys-runtime" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/chat-ai-keys-runtime" }} export CHAT_KEY_MATRIX="{{ .Data.data.matrix }}" export CHAT_KEY_HOMEPAGE="{{ .Data.data.homepage }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/portal-e2e-client" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/portal-e2e-client" }} export PORTAL_E2E_CLIENT_ID="{{ .Data.data.client_id }}" export PORTAL_E2E_CLIENT_SECRET="{{ .Data.data.client_secret }}" - {{- end }} + {{ end }} spec: serviceAccountName: bstein-dev-home restartPolicy: Never diff --git a/services/gitea/deployment.yaml b/services/gitea/deployment.yaml index cbcdab8..69650ca 100644 --- a/services/gitea/deployment.yaml +++ b/services/gitea/deployment.yaml @@ -26,34 +26,34 @@ spec: vault.hashicorp.com/role: "gitea" vault.hashicorp.com/agent-inject-secret-gitea-db-secret__password: "kv/data/atlas/gitea/gitea-db-secret" vault.hashicorp.com/agent-inject-template-gitea-db-secret__password: | - {{- with secret "kv/data/atlas/gitea/gitea-db-secret" -}} + {{ with secret "kv/data/atlas/gitea/gitea-db-secret" }} {{ .Data.data.password }} - {{- end }} + {{ end }} vault.hashicorp.com/agent-inject-secret-gitea-secret__SECRET_KEY: "kv/data/atlas/gitea/gitea-secret" vault.hashicorp.com/agent-inject-template-gitea-secret__SECRET_KEY: | - {{- with secret "kv/data/atlas/gitea/gitea-secret" -}} + {{ with secret "kv/data/atlas/gitea/gitea-secret" }} {{ .Data.data.SECRET_KEY }} - {{- end }} + {{ end }} vault.hashicorp.com/agent-inject-secret-gitea-secret__INTERNAL_TOKEN: "kv/data/atlas/gitea/gitea-secret" vault.hashicorp.com/agent-inject-template-gitea-secret__INTERNAL_TOKEN: | - {{- with secret "kv/data/atlas/gitea/gitea-secret" -}} + {{ with secret "kv/data/atlas/gitea/gitea-secret" }} {{ .Data.data.INTERNAL_TOKEN }} - {{- end }} + {{ end }} vault.hashicorp.com/agent-inject-secret-gitea-oidc__client_id: "kv/data/atlas/gitea/gitea-oidc" vault.hashicorp.com/agent-inject-template-gitea-oidc__client_id: | - {{- with secret "kv/data/atlas/gitea/gitea-oidc" -}} + {{ with secret "kv/data/atlas/gitea/gitea-oidc" }} {{ .Data.data.client_id }} - {{- end }} + {{ end }} vault.hashicorp.com/agent-inject-secret-gitea-oidc__client_secret: "kv/data/atlas/gitea/gitea-oidc" vault.hashicorp.com/agent-inject-template-gitea-oidc__client_secret: | - {{- with secret "kv/data/atlas/gitea/gitea-oidc" -}} + {{ with secret "kv/data/atlas/gitea/gitea-oidc" }} {{ .Data.data.client_secret }} - {{- end }} + {{ end }} vault.hashicorp.com/agent-inject-secret-gitea-oidc__openid_auto_discovery_url: "kv/data/atlas/gitea/gitea-oidc" vault.hashicorp.com/agent-inject-template-gitea-oidc__openid_auto_discovery_url: | - {{- with secret "kv/data/atlas/gitea/gitea-oidc" -}} + {{ with secret "kv/data/atlas/gitea/gitea-oidc" }} {{ .Data.data.openid_auto_discovery_url }} - {{- end }} + {{ end }} spec: serviceAccountName: gitea-vault initContainers: diff --git a/services/health/endurain-data-pvc.yaml b/services/health/endurain-data-pvc.yaml new file mode 100644 index 0000000..6c8d244 --- /dev/null +++ b/services/health/endurain-data-pvc.yaml @@ -0,0 +1,12 @@ +# services/health/endurain-data-pvc.yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: endurain-data + namespace: health +spec: + accessModes: ["ReadWriteOnce"] + storageClassName: asteria + resources: + requests: + storage: 10Gi diff --git a/services/health/endurain-deployment.yaml b/services/health/endurain-deployment.yaml new file mode 100644 index 0000000..05608b1 --- /dev/null +++ b/services/health/endurain-deployment.yaml @@ -0,0 +1,147 @@ +# services/health/endurain-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: endurain + namespace: health + labels: + app: endurain +spec: + replicas: 1 + selector: + matchLabels: + app: endurain + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 0 + maxUnavailable: 1 + template: + metadata: + labels: + app: endurain + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: hardware + operator: In + values: ["rpi5", "rpi4"] + - key: node-role.kubernetes.io/worker + operator: Exists + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 90 + preference: + matchExpressions: + - key: hardware + operator: In + values: ["rpi5"] + - weight: 70 + preference: + matchExpressions: + - key: hardware + operator: In + values: ["rpi4"] + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + fsGroupChangePolicy: OnRootMismatch + initContainers: + - name: init-data + image: alpine:3.20 + command: ["/bin/sh", "-c"] + args: + - | + set -e + mkdir -p /data + chown -R 1000:1000 /data + securityContext: + runAsUser: 0 + runAsGroup: 0 + volumeMounts: + - name: endurain-data + mountPath: /data + containers: + - name: endurain + image: ghcr.io/endurain-project/endurain:v0.16.6 + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 8080 + env: + - name: ENDURAIN_HOST + value: https://endurain.bstein.dev + - name: BEHIND_PROXY + value: "true" + - name: LOG_LEVEL + value: info + - name: TZ + value: Etc/UTC + - name: DB_HOST + valueFrom: + secretKeyRef: + name: endurain-db + key: DB_HOST + - name: DB_PORT + valueFrom: + secretKeyRef: + name: endurain-db + key: DB_PORT + - name: DB_USER + valueFrom: + secretKeyRef: + name: endurain-db + key: DB_USER + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: endurain-db + key: DB_PASSWORD + - name: DB_DATABASE + valueFrom: + secretKeyRef: + name: endurain-db + key: DB_DATABASE + - name: SECRET_KEY + valueFrom: + secretKeyRef: + name: endurain-secrets + key: SECRET_KEY + - name: FERNET_KEY + valueFrom: + secretKeyRef: + name: endurain-secrets + key: FERNET_KEY + volumeMounts: + - name: endurain-data + mountPath: /app/backend/data + readinessProbe: + httpGet: + path: /api/v1/about + port: http + initialDelaySeconds: 15 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 6 + livenessProbe: + httpGet: + path: /api/v1/about + port: http + initialDelaySeconds: 30 + periodSeconds: 20 + timeoutSeconds: 3 + failureThreshold: 6 + resources: + requests: + cpu: 200m + memory: 512Mi + limits: + cpu: "1" + memory: 2Gi + volumes: + - name: endurain-data + persistentVolumeClaim: + claimName: endurain-data diff --git a/services/health/endurain-ingress.yaml b/services/health/endurain-ingress.yaml new file mode 100644 index 0000000..a7b2cc0 --- /dev/null +++ b/services/health/endurain-ingress.yaml @@ -0,0 +1,26 @@ +# services/health/endurain-ingress.yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: endurain + namespace: health + annotations: + kubernetes.io/ingress.class: traefik + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + cert-manager.io/cluster-issuer: letsencrypt +spec: + tls: + - hosts: ["endurain.bstein.dev"] + secretName: endurain-tls + rules: + - host: endurain.bstein.dev + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: endurain + port: + number: 80 diff --git a/services/health/endurain-oidc-config-cronjob.yaml b/services/health/endurain-oidc-config-cronjob.yaml new file mode 100644 index 0000000..7930425 --- /dev/null +++ b/services/health/endurain-oidc-config-cronjob.yaml @@ -0,0 +1,87 @@ +# services/health/endurain-oidc-config-cronjob.yaml +apiVersion: batch/v1 +kind: CronJob +metadata: + name: endurain-oidc-config + namespace: health +spec: + schedule: "*/30 * * * *" + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 1 + failedJobsHistoryLimit: 3 + jobTemplate: + spec: + backoffLimit: 1 + template: + spec: + serviceAccountName: health-vault-sync + restartPolicy: Never + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/arch + operator: In + values: ["arm64"] + - key: node-role.kubernetes.io/worker + operator: Exists + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 90 + preference: + matchExpressions: + - key: hardware + operator: In + values: ["rpi5"] + - weight: 70 + preference: + matchExpressions: + - key: hardware + operator: In + values: ["rpi4"] + containers: + - name: configure + image: alpine:3.20 + command: ["/bin/sh", "-c"] + args: + - | + set -euo pipefail + apk add --no-cache bash curl jq >/dev/null + exec /scripts/endurain_oidc_configure.sh + env: + - name: ENDURAIN_BASE_URL + value: http://endurain.health.svc.cluster.local + - name: ENDURAIN_ADMIN_USERNAME + valueFrom: + secretKeyRef: + name: endurain-admin + key: username + - name: ENDURAIN_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: endurain-admin + key: password + - name: ENDURAIN_OIDC_CLIENT_ID + valueFrom: + secretKeyRef: + name: endurain-oidc + key: client_id + - name: ENDURAIN_OIDC_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: endurain-oidc + key: client_secret + - name: ENDURAIN_OIDC_ISSUER_URL + valueFrom: + secretKeyRef: + name: endurain-oidc + key: issuer_url + volumeMounts: + - name: endurain-oidc-config-script + mountPath: /scripts + readOnly: true + volumes: + - name: endurain-oidc-config-script + configMap: + name: endurain-oidc-config-script + defaultMode: 0555 diff --git a/services/health/endurain-service.yaml b/services/health/endurain-service.yaml new file mode 100644 index 0000000..cffe116 --- /dev/null +++ b/services/health/endurain-service.yaml @@ -0,0 +1,15 @@ +# services/health/endurain-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: endurain + namespace: health + labels: + app: endurain +spec: + selector: + app: endurain + ports: + - name: http + port: 80 + targetPort: http diff --git a/services/health/kustomization.yaml b/services/health/kustomization.yaml new file mode 100644 index 0000000..1690876 --- /dev/null +++ b/services/health/kustomization.yaml @@ -0,0 +1,30 @@ +# services/health/kustomization.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: health +resources: + - namespace.yaml + - serviceaccount.yaml + - secretproviderclass.yaml + - vault-sync-deployment.yaml + - endurain-data-pvc.yaml + - sparkyfitness-data-pvc.yaml + - endurain-oidc-config-cronjob.yaml + - sparkyfitness-oidc-config-cronjob.yaml + - endurain-deployment.yaml + - endurain-service.yaml + - sparkyfitness-server-deployment.yaml + - sparkyfitness-server-service.yaml + - sparkyfitness-frontend-deployment.yaml + - sparkyfitness-frontend-service.yaml + - endurain-ingress.yaml + - sparkyfitness-ingress.yaml +generatorOptions: + disableNameSuffixHash: true +configMapGenerator: + - name: endurain-oidc-config-script + files: + - endurain_oidc_configure.sh=scripts/endurain_oidc_configure.sh + - name: sparkyfitness-oidc-config-script + files: + - sparkyfitness_oidc_configure.sh=scripts/sparkyfitness_oidc_configure.sh diff --git a/services/health/namespace.yaml b/services/health/namespace.yaml new file mode 100644 index 0000000..71d6fff --- /dev/null +++ b/services/health/namespace.yaml @@ -0,0 +1,5 @@ +# services/health/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: health diff --git a/services/health/scripts/endurain_oidc_configure.sh b/services/health/scripts/endurain_oidc_configure.sh new file mode 100644 index 0000000..76ebc99 --- /dev/null +++ b/services/health/scripts/endurain_oidc_configure.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash +set -euo pipefail + +base_url="${ENDURAIN_BASE_URL:-http://endurain.health.svc.cluster.local}" +admin_username="${ENDURAIN_ADMIN_USERNAME:-admin}" +admin_password="${ENDURAIN_ADMIN_PASSWORD:?ENDURAIN_ADMIN_PASSWORD is required}" +default_password="${ENDURAIN_DEFAULT_ADMIN_PASSWORD:-admin}" +oidc_client_id="${ENDURAIN_OIDC_CLIENT_ID:?ENDURAIN_OIDC_CLIENT_ID is required}" +oidc_client_secret="${ENDURAIN_OIDC_CLIENT_SECRET:?ENDURAIN_OIDC_CLIENT_SECRET is required}" +oidc_issuer_url="${ENDURAIN_OIDC_ISSUER_URL:?ENDURAIN_OIDC_ISSUER_URL is required}" + +wait_for_endurain() { + for attempt in 1 2 3 4 5 6 7 8 9 10; do + if curl -fsS "${base_url}/api/v1/about" >/dev/null 2>&1; then + return 0 + fi + sleep $((attempt * 3)) + done + return 1 +} + +login() { + local username="$1" + local password="$2" + local token + token="$(curl -sS -X POST "${base_url}/api/v1/auth/login" \ + -H "X-Client-Type: mobile" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + --data-urlencode "grant_type=password" \ + --data-urlencode "username=${username}" \ + --data-urlencode "password=${password}" | jq -r '.access_token' 2>/dev/null || true)" + if [ -n "${token}" ] && [ "${token}" != "null" ]; then + echo "${token}" + return 0 + fi + return 1 +} + +if ! wait_for_endurain; then + echo "Endurain is not responding at ${base_url}" >&2 + exit 1 +fi + +token="$(login "${admin_username}" "${admin_password}" || true)" +if [ -z "${token}" ]; then + token="$(login "${admin_username}" "${default_password}" || true)" + if [ -z "${token}" ]; then + echo "Failed to authenticate to Endurain as admin" >&2 + exit 1 + fi + if [ "${admin_password}" != "${default_password}" ]; then + user_id="$(curl -sS -H "Authorization: Bearer ${token}" -H "X-Client-Type: mobile" \ + "${base_url}/api/v1/users/username/${admin_username}" | jq -r '.id' 2>/dev/null || true)" + if [ -z "${user_id}" ] || [ "${user_id}" = "null" ]; then + echo "Admin user ${admin_username} not found" >&2 + exit 1 + fi + update_payload="$(jq -nc --arg password "${admin_password}" '{password:$password}')" + status="$(curl -sS -o /dev/null -w "%{http_code}" -X PUT \ + -H "Authorization: Bearer ${token}" \ + -H "X-Client-Type: mobile" \ + -H "Content-Type: application/json" \ + -d "${update_payload}" \ + "${base_url}/api/v1/users/${user_id}/password")" + if [ "${status}" != "200" ] && [ "${status}" != "201" ]; then + echo "Failed to rotate Endurain admin password (status ${status})" >&2 + exit 1 + fi + token="$(login "${admin_username}" "${admin_password}" || true)" + if [ -z "${token}" ]; then + echo "Failed to authenticate with rotated admin password" >&2 + exit 1 + fi + fi +fi + +idp_payload="$(jq -nc \ + --arg name "Keycloak" \ + --arg slug "keycloak" \ + --arg issuer_url "${oidc_issuer_url}" \ + --arg scopes "openid profile email" \ + --arg client_id "${oidc_client_id}" \ + --arg client_secret "${oidc_client_secret}" \ + --arg icon "keycloak" \ + --argjson enabled true \ + --argjson auto_create_users true \ + --argjson sync_user_info true \ + --argjson user_mapping '{"username":["preferred_username","username","email"],"email":["email","mail"],"name":["name","display_name","full_name"]}' \ + '{name:$name,slug:$slug,provider_type:"oidc",enabled:$enabled,issuer_url:$issuer_url,scopes:$scopes,icon:$icon,auto_create_users:$auto_create_users,sync_user_info:$sync_user_info,user_mapping:$user_mapping,client_id:$client_id,client_secret:$client_secret}')" + +idp_id="$(curl -sS -H "Authorization: Bearer ${token}" -H "X-Client-Type: mobile" \ + "${base_url}/api/v1/idp" | jq -r '.[] | select(.slug=="keycloak") | .id' 2>/dev/null | head -n1 || true)" + +if [ -n "${idp_id}" ] && [ "${idp_id}" != "null" ]; then + status="$(curl -sS -o /dev/null -w "%{http_code}" -X PUT \ + -H "Authorization: Bearer ${token}" \ + -H "X-Client-Type: mobile" \ + -H "Content-Type: application/json" \ + -d "${idp_payload}" \ + "${base_url}/api/v1/idp/${idp_id}")" +else + status="$(curl -sS -o /dev/null -w "%{http_code}" -X POST \ + -H "Authorization: Bearer ${token}" \ + -H "X-Client-Type: mobile" \ + -H "Content-Type: application/json" \ + -d "${idp_payload}" \ + "${base_url}/api/v1/idp")" +fi + +if [ "${status}" != "200" ] && [ "${status}" != "201" ] && [ "${status}" != "204" ]; then + echo "Failed to upsert Endurain OIDC provider (status ${status})" >&2 + exit 1 +fi + +settings_json="$(curl -sS -H "Authorization: Bearer ${token}" -H "X-Client-Type: mobile" \ + "${base_url}/api/v1/server_settings")" +if [ -z "${settings_json}" ]; then + echo "Failed to fetch Endurain server settings" >&2 + exit 1 +fi + +settings_payload="$(echo "${settings_json}" | jq \ + '.sso_enabled=true | .sso_auto_redirect=true | .signup_enabled=false | .local_login_enabled=true')" + +status="$(curl -sS -o /dev/null -w "%{http_code}" -X PUT \ + -H "Authorization: Bearer ${token}" \ + -H "X-Client-Type: mobile" \ + -H "Content-Type: application/json" \ + -d "${settings_payload}" \ + "${base_url}/api/v1/server_settings")" +if [ "${status}" != "200" ] && [ "${status}" != "201" ]; then + echo "Failed to update Endurain server settings (status ${status})" >&2 + exit 1 +fi diff --git a/services/health/scripts/sparkyfitness_oidc_configure.sh b/services/health/scripts/sparkyfitness_oidc_configure.sh new file mode 100644 index 0000000..98c6857 --- /dev/null +++ b/services/health/scripts/sparkyfitness_oidc_configure.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash +set -euo pipefail + +base_url="${SPARKYFITNESS_BASE_URL:-http://sparkyfitness-server.health.svc.cluster.local:3010}" +frontend_url="${SPARKYFITNESS_FRONTEND_URL:?SPARKYFITNESS_FRONTEND_URL is required}" +admin_email="${SPARKYFITNESS_ADMIN_EMAIL:?SPARKYFITNESS_ADMIN_EMAIL is required}" +admin_password="${SPARKYFITNESS_ADMIN_PASSWORD:?SPARKYFITNESS_ADMIN_PASSWORD is required}" +oidc_client_id="${SPARKYFITNESS_OIDC_CLIENT_ID:?SPARKYFITNESS_OIDC_CLIENT_ID is required}" +oidc_client_secret="${SPARKYFITNESS_OIDC_CLIENT_SECRET:?SPARKYFITNESS_OIDC_CLIENT_SECRET is required}" +oidc_issuer_url="${SPARKYFITNESS_OIDC_ISSUER_URL:?SPARKYFITNESS_OIDC_ISSUER_URL is required}" + +wait_for_server() { + for attempt in 1 2 3 4 5 6 7 8 9 10; do + if curl -fsS "${base_url}/health" >/dev/null 2>&1; then + return 0 + fi + sleep $((attempt * 3)) + done + return 1 +} + +cookie_jar="$(mktemp)" +trap 'rm -f "${cookie_jar}"' EXIT + +auth_login() { + local payload + payload="$(jq -nc --arg email "${admin_email}" --arg password "${admin_password}" '{email:$email,password:$password}')" + local status + status="$(curl -sS -o /tmp/sparkyfitness_login.json -w "%{http_code}" \ + -c "${cookie_jar}" -b "${cookie_jar}" \ + -H "Content-Type: application/json" \ + -X POST "${base_url}/auth/login" \ + -d "${payload}")" + if [ "${status}" = "200" ]; then + return 0 + fi + return 1 +} + +auth_register() { + local payload + payload="$(jq -nc --arg email "${admin_email}" --arg password "${admin_password}" --arg full_name "Sparky Admin" '{email:$email,password:$password,full_name:$full_name}')" + curl -sS -o /tmp/sparkyfitness_register.json -w "%{http_code}" \ + -c "${cookie_jar}" -b "${cookie_jar}" \ + -H "Content-Type: application/json" \ + -X POST "${base_url}/auth/register" \ + -d "${payload}" +} + +if ! wait_for_server; then + echo "SparkyFitness is not responding at ${base_url}" >&2 + exit 1 +fi + +if ! auth_login; then + status="$(auth_register)" + if [ "${status}" = "409" ]; then + if ! auth_login; then + echo "Admin login failed after existing user detected" >&2 + exit 1 + fi + elif [ "${status}" = "201" ]; then + if ! auth_login; then + echo "Admin login failed after registration" >&2 + exit 1 + fi + elif [ "${status}" = "403" ]; then + echo "Registration disabled; unable to bootstrap admin user" >&2 + exit 1 + else + echo "Admin registration failed (status ${status})" >&2 + exit 1 + fi +fi + +settings_json="$(curl -sS -b "${cookie_jar}" "${base_url}/admin/global-settings")" +if [ -z "${settings_json}" ]; then + echo "Failed to fetch SparkyFitness global settings" >&2 + exit 1 +fi + +email_enabled="$(echo "${settings_json}" | jq -r '.enable_email_password_login // true')" +mfa_mandatory="$(echo "${settings_json}" | jq -r '.is_mfa_mandatory // .mfa_mandatory // false')" +settings_payload="$(jq -nc \ + --argjson enable_email_password_login "${email_enabled}" \ + --argjson is_oidc_active true \ + --argjson is_mfa_mandatory "${mfa_mandatory}" \ + '{enable_email_password_login:$enable_email_password_login,is_oidc_active:$is_oidc_active,is_mfa_mandatory:$is_mfa_mandatory}')" + +status="$(curl -sS -o /dev/null -w "%{http_code}" -b "${cookie_jar}" \ + -H "Content-Type: application/json" \ + -X PUT "${base_url}/admin/global-settings" \ + -d "${settings_payload}")" +if [ "${status}" != "200" ]; then + echo "Failed to update SparkyFitness global settings (status ${status})" >&2 + exit 1 +fi + +providers_json="$(curl -sS -b "${cookie_jar}" "${base_url}/admin/oidc-settings")" +provider_id="$(echo "${providers_json}" | jq -r --arg issuer "${oidc_issuer_url}" '.[] | select(.issuer_url==$issuer) | .id' 2>/dev/null | head -n1 || true)" + +redirect_uri="${frontend_url%/}/oidc-callback" +provider_payload="$(jq -nc \ + --arg issuer_url "${oidc_issuer_url}" \ + --arg client_id "${oidc_client_id}" \ + --arg client_secret "${oidc_client_secret}" \ + --arg redirect_uri "${redirect_uri}" \ + --arg scope "openid profile email" \ + --arg token_endpoint_auth_method "client_secret_post" \ + --argjson response_types '["code"]' \ + --argjson is_active true \ + --arg display_name "Atlas SSO" \ + --argjson auto_register true \ + --arg signing_algorithm "RS256" \ + --arg profile_signing_algorithm "none" \ + --argjson timeout 30000 \ + '{issuer_url:$issuer_url,client_id:$client_id,client_secret:$client_secret,redirect_uris:[$redirect_uri],scope:$scope,token_endpoint_auth_method:$token_endpoint_auth_method,response_types:$response_types,is_active:$is_active,display_name:$display_name,auto_register:$auto_register,signing_algorithm:$signing_algorithm,profile_signing_algorithm:$profile_signing_algorithm,timeout:$timeout}')" + +if [ -n "${provider_id}" ] && [ "${provider_id}" != "null" ]; then + status="$(curl -sS -o /dev/null -w "%{http_code}" -b "${cookie_jar}" \ + -H "Content-Type: application/json" \ + -X PUT "${base_url}/admin/oidc-settings/${provider_id}" \ + -d "${provider_payload}")" +else + status="$(curl -sS -o /dev/null -w "%{http_code}" -b "${cookie_jar}" \ + -H "Content-Type: application/json" \ + -X POST "${base_url}/admin/oidc-settings" \ + -d "${provider_payload}")" +fi + +if [ "${status}" != "200" ] && [ "${status}" != "201" ]; then + echo "Failed to upsert SparkyFitness OIDC provider (status ${status})" >&2 + exit 1 +fi diff --git a/services/health/secretproviderclass.yaml b/services/health/secretproviderclass.yaml new file mode 100644 index 0000000..c019c96 --- /dev/null +++ b/services/health/secretproviderclass.yaml @@ -0,0 +1,167 @@ +# services/health/secretproviderclass.yaml +apiVersion: secrets-store.csi.x-k8s.io/v1 +kind: SecretProviderClass +metadata: + name: health-vault + namespace: health +spec: + provider: vault + parameters: + vaultAddress: "http://vault.vault.svc.cluster.local:8200" + roleName: "health" + objects: | + - objectName: "endurain-db__DB_HOST" + secretPath: "kv/data/atlas/health/endurain-db" + secretKey: "DB_HOST" + - objectName: "endurain-db__DB_PORT" + secretPath: "kv/data/atlas/health/endurain-db" + secretKey: "DB_PORT" + - objectName: "endurain-db__DB_USER" + secretPath: "kv/data/atlas/health/endurain-db" + secretKey: "DB_USER" + - objectName: "endurain-db__DB_PASSWORD" + secretPath: "kv/data/atlas/health/endurain-db" + secretKey: "DB_PASSWORD" + - objectName: "endurain-db__DB_DATABASE" + secretPath: "kv/data/atlas/health/endurain-db" + secretKey: "DB_DATABASE" + - objectName: "endurain-secrets__SECRET_KEY" + secretPath: "kv/data/atlas/health/endurain-secrets" + secretKey: "SECRET_KEY" + - objectName: "endurain-secrets__FERNET_KEY" + secretPath: "kv/data/atlas/health/endurain-secrets" + secretKey: "FERNET_KEY" + - objectName: "endurain-admin__username" + secretPath: "kv/data/atlas/health/endurain-admin" + secretKey: "username" + - objectName: "endurain-admin__password" + secretPath: "kv/data/atlas/health/endurain-admin" + secretKey: "password" + - objectName: "endurain-oidc__client_id" + secretPath: "kv/data/atlas/health/endurain-oidc" + secretKey: "client_id" + - objectName: "endurain-oidc__client_secret" + secretPath: "kv/data/atlas/health/endurain-oidc" + secretKey: "client_secret" + - objectName: "endurain-oidc__issuer_url" + secretPath: "kv/data/atlas/health/endurain-oidc" + secretKey: "issuer_url" + - objectName: "sparkyfitness-db__SPARKY_FITNESS_DB_HOST" + secretPath: "kv/data/atlas/health/sparkyfitness-db" + secretKey: "SPARKY_FITNESS_DB_HOST" + - objectName: "sparkyfitness-db__SPARKY_FITNESS_DB_PORT" + secretPath: "kv/data/atlas/health/sparkyfitness-db" + secretKey: "SPARKY_FITNESS_DB_PORT" + - objectName: "sparkyfitness-db__SPARKY_FITNESS_DB_NAME" + secretPath: "kv/data/atlas/health/sparkyfitness-db" + secretKey: "SPARKY_FITNESS_DB_NAME" + - objectName: "sparkyfitness-db__SPARKY_FITNESS_DB_USER" + secretPath: "kv/data/atlas/health/sparkyfitness-db" + secretKey: "SPARKY_FITNESS_DB_USER" + - objectName: "sparkyfitness-db__SPARKY_FITNESS_DB_PASSWORD" + secretPath: "kv/data/atlas/health/sparkyfitness-db" + secretKey: "SPARKY_FITNESS_DB_PASSWORD" + - objectName: "sparkyfitness-db__SPARKY_FITNESS_APP_DB_USER" + secretPath: "kv/data/atlas/health/sparkyfitness-db" + secretKey: "SPARKY_FITNESS_APP_DB_USER" + - objectName: "sparkyfitness-db__SPARKY_FITNESS_APP_DB_PASSWORD" + secretPath: "kv/data/atlas/health/sparkyfitness-db" + secretKey: "SPARKY_FITNESS_APP_DB_PASSWORD" + - objectName: "sparkyfitness-secrets__JWT_SECRET" + secretPath: "kv/data/atlas/health/sparkyfitness-secrets" + secretKey: "JWT_SECRET" + - objectName: "sparkyfitness-secrets__SPARKY_FITNESS_API_ENCRYPTION_KEY" + secretPath: "kv/data/atlas/health/sparkyfitness-secrets" + secretKey: "SPARKY_FITNESS_API_ENCRYPTION_KEY" + - objectName: "sparkyfitness-admin__email" + secretPath: "kv/data/atlas/health/sparkyfitness-admin" + secretKey: "email" + - objectName: "sparkyfitness-admin__password" + secretPath: "kv/data/atlas/health/sparkyfitness-admin" + secretKey: "password" + - objectName: "sparkyfitness-oidc__client_id" + secretPath: "kv/data/atlas/health/sparkyfitness-oidc" + secretKey: "client_id" + - objectName: "sparkyfitness-oidc__client_secret" + secretPath: "kv/data/atlas/health/sparkyfitness-oidc" + secretKey: "client_secret" + - objectName: "sparkyfitness-oidc__issuer_url" + secretPath: "kv/data/atlas/health/sparkyfitness-oidc" + secretKey: "issuer_url" + secretObjects: + - secretName: endurain-db + type: Opaque + data: + - objectName: endurain-db__DB_HOST + key: DB_HOST + - objectName: endurain-db__DB_PORT + key: DB_PORT + - objectName: endurain-db__DB_USER + key: DB_USER + - objectName: endurain-db__DB_PASSWORD + key: DB_PASSWORD + - objectName: endurain-db__DB_DATABASE + key: DB_DATABASE + - secretName: endurain-secrets + type: Opaque + data: + - objectName: endurain-secrets__SECRET_KEY + key: SECRET_KEY + - objectName: endurain-secrets__FERNET_KEY + key: FERNET_KEY + - secretName: endurain-admin + type: Opaque + data: + - objectName: endurain-admin__username + key: username + - objectName: endurain-admin__password + key: password + - secretName: endurain-oidc + type: Opaque + data: + - objectName: endurain-oidc__client_id + key: client_id + - objectName: endurain-oidc__client_secret + key: client_secret + - objectName: endurain-oidc__issuer_url + key: issuer_url + - secretName: sparkyfitness-db + type: Opaque + data: + - objectName: sparkyfitness-db__SPARKY_FITNESS_DB_HOST + key: SPARKY_FITNESS_DB_HOST + - objectName: sparkyfitness-db__SPARKY_FITNESS_DB_PORT + key: SPARKY_FITNESS_DB_PORT + - objectName: sparkyfitness-db__SPARKY_FITNESS_DB_NAME + key: SPARKY_FITNESS_DB_NAME + - objectName: sparkyfitness-db__SPARKY_FITNESS_DB_USER + key: SPARKY_FITNESS_DB_USER + - objectName: sparkyfitness-db__SPARKY_FITNESS_DB_PASSWORD + key: SPARKY_FITNESS_DB_PASSWORD + - objectName: sparkyfitness-db__SPARKY_FITNESS_APP_DB_USER + key: SPARKY_FITNESS_APP_DB_USER + - objectName: sparkyfitness-db__SPARKY_FITNESS_APP_DB_PASSWORD + key: SPARKY_FITNESS_APP_DB_PASSWORD + - secretName: sparkyfitness-secrets + type: Opaque + data: + - objectName: sparkyfitness-secrets__JWT_SECRET + key: JWT_SECRET + - objectName: sparkyfitness-secrets__SPARKY_FITNESS_API_ENCRYPTION_KEY + key: SPARKY_FITNESS_API_ENCRYPTION_KEY + - secretName: sparkyfitness-admin + type: Opaque + data: + - objectName: sparkyfitness-admin__email + key: email + - objectName: sparkyfitness-admin__password + key: password + - secretName: sparkyfitness-oidc + type: Opaque + data: + - objectName: sparkyfitness-oidc__client_id + key: client_id + - objectName: sparkyfitness-oidc__client_secret + key: client_secret + - objectName: sparkyfitness-oidc__issuer_url + key: issuer_url diff --git a/services/health/serviceaccount.yaml b/services/health/serviceaccount.yaml new file mode 100644 index 0000000..78046ba --- /dev/null +++ b/services/health/serviceaccount.yaml @@ -0,0 +1,6 @@ +# services/health/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: health-vault-sync + namespace: health diff --git a/services/health/sparkyfitness-data-pvc.yaml b/services/health/sparkyfitness-data-pvc.yaml new file mode 100644 index 0000000..0fbcf7b --- /dev/null +++ b/services/health/sparkyfitness-data-pvc.yaml @@ -0,0 +1,12 @@ +# services/health/sparkyfitness-data-pvc.yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: sparkyfitness-data + namespace: health +spec: + accessModes: ["ReadWriteOnce"] + storageClassName: asteria + resources: + requests: + storage: 10Gi diff --git a/services/health/sparkyfitness-frontend-deployment.yaml b/services/health/sparkyfitness-frontend-deployment.yaml new file mode 100644 index 0000000..38df36a --- /dev/null +++ b/services/health/sparkyfitness-frontend-deployment.yaml @@ -0,0 +1,81 @@ +# services/health/sparkyfitness-frontend-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sparkyfitness-frontend + namespace: health + labels: + app: sparkyfitness-frontend +spec: + replicas: 1 + selector: + matchLabels: + app: sparkyfitness-frontend + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 0 + maxUnavailable: 1 + template: + metadata: + labels: + app: sparkyfitness-frontend + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: hardware + operator: In + values: ["rpi5", "rpi4"] + - key: node-role.kubernetes.io/worker + operator: Exists + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 90 + preference: + matchExpressions: + - key: hardware + operator: In + values: ["rpi5"] + - weight: 70 + preference: + matchExpressions: + - key: hardware + operator: In + values: ["rpi4"] + containers: + - name: sparkyfitness-frontend + image: codewithcj/sparkyfitness:0.16.3.3 + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 80 + env: + - name: SPARKY_FITNESS_SERVER_HOST + value: sparkyfitness-server + - name: SPARKY_FITNESS_SERVER_PORT + value: "3010" + readinessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 6 + livenessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 30 + periodSeconds: 20 + timeoutSeconds: 3 + failureThreshold: 6 + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi diff --git a/services/health/sparkyfitness-frontend-service.yaml b/services/health/sparkyfitness-frontend-service.yaml new file mode 100644 index 0000000..0850d6c --- /dev/null +++ b/services/health/sparkyfitness-frontend-service.yaml @@ -0,0 +1,15 @@ +# services/health/sparkyfitness-frontend-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: sparkyfitness-frontend + namespace: health + labels: + app: sparkyfitness-frontend +spec: + selector: + app: sparkyfitness-frontend + ports: + - name: http + port: 80 + targetPort: http diff --git a/services/health/sparkyfitness-ingress.yaml b/services/health/sparkyfitness-ingress.yaml new file mode 100644 index 0000000..b9d5758 --- /dev/null +++ b/services/health/sparkyfitness-ingress.yaml @@ -0,0 +1,26 @@ +# services/health/sparkyfitness-ingress.yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: sparkyfitness + namespace: health + annotations: + kubernetes.io/ingress.class: traefik + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + cert-manager.io/cluster-issuer: letsencrypt +spec: + tls: + - hosts: ["sparkyfitness.bstein.dev"] + secretName: sparkyfitness-tls + rules: + - host: sparkyfitness.bstein.dev + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: sparkyfitness-frontend + port: + number: 80 diff --git a/services/health/sparkyfitness-oidc-config-cronjob.yaml b/services/health/sparkyfitness-oidc-config-cronjob.yaml new file mode 100644 index 0000000..a20c1f1 --- /dev/null +++ b/services/health/sparkyfitness-oidc-config-cronjob.yaml @@ -0,0 +1,89 @@ +# services/health/sparkyfitness-oidc-config-cronjob.yaml +apiVersion: batch/v1 +kind: CronJob +metadata: + name: sparkyfitness-oidc-config + namespace: health +spec: + schedule: "*/30 * * * *" + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 1 + failedJobsHistoryLimit: 3 + jobTemplate: + spec: + backoffLimit: 1 + template: + spec: + serviceAccountName: health-vault-sync + restartPolicy: Never + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/arch + operator: In + values: ["arm64"] + - key: node-role.kubernetes.io/worker + operator: Exists + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 90 + preference: + matchExpressions: + - key: hardware + operator: In + values: ["rpi5"] + - weight: 70 + preference: + matchExpressions: + - key: hardware + operator: In + values: ["rpi4"] + containers: + - name: configure + image: alpine:3.20 + command: ["/bin/sh", "-c"] + args: + - | + set -euo pipefail + apk add --no-cache bash curl jq >/dev/null + exec /scripts/sparkyfitness_oidc_configure.sh + env: + - name: SPARKYFITNESS_BASE_URL + value: http://sparkyfitness-server.health.svc.cluster.local:3010 + - name: SPARKYFITNESS_FRONTEND_URL + value: https://sparkyfitness.bstein.dev + - name: SPARKYFITNESS_ADMIN_EMAIL + valueFrom: + secretKeyRef: + name: sparkyfitness-admin + key: email + - name: SPARKYFITNESS_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: sparkyfitness-admin + key: password + - name: SPARKYFITNESS_OIDC_CLIENT_ID + valueFrom: + secretKeyRef: + name: sparkyfitness-oidc + key: client_id + - name: SPARKYFITNESS_OIDC_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: sparkyfitness-oidc + key: client_secret + - name: SPARKYFITNESS_OIDC_ISSUER_URL + valueFrom: + secretKeyRef: + name: sparkyfitness-oidc + key: issuer_url + volumeMounts: + - name: sparkyfitness-oidc-config-script + mountPath: /scripts + readOnly: true + volumes: + - name: sparkyfitness-oidc-config-script + configMap: + name: sparkyfitness-oidc-config-script + defaultMode: 0555 diff --git a/services/health/sparkyfitness-server-deployment.yaml b/services/health/sparkyfitness-server-deployment.yaml new file mode 100644 index 0000000..e920662 --- /dev/null +++ b/services/health/sparkyfitness-server-deployment.yaml @@ -0,0 +1,170 @@ +# services/health/sparkyfitness-server-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sparkyfitness-server + namespace: health + labels: + app: sparkyfitness-server +spec: + replicas: 1 + selector: + matchLabels: + app: sparkyfitness-server + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 0 + maxUnavailable: 1 + template: + metadata: + labels: + app: sparkyfitness-server + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: hardware + operator: In + values: ["rpi5", "rpi4"] + - key: node-role.kubernetes.io/worker + operator: Exists + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 90 + preference: + matchExpressions: + - key: hardware + operator: In + values: ["rpi5"] + - weight: 70 + preference: + matchExpressions: + - key: hardware + operator: In + values: ["rpi4"] + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + fsGroupChangePolicy: OnRootMismatch + initContainers: + - name: init-data + image: alpine:3.20 + command: ["/bin/sh", "-c"] + args: + - | + set -e + mkdir -p /data/uploads /data/backup + chown -R 1000:1000 /data + securityContext: + runAsUser: 0 + runAsGroup: 0 + volumeMounts: + - name: sparkyfitness-data + mountPath: /data + containers: + - name: sparkyfitness-server + image: codewithcj/sparkyfitness_server:0.16.3.3 + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 3010 + env: + - name: SPARKY_FITNESS_SERVER_PORT + value: "3010" + - name: SPARKY_FITNESS_LOG_LEVEL + value: INFO + - name: NODE_ENV + value: production + - name: TZ + value: Etc/UTC + - name: SPARKY_FITNESS_FRONTEND_URL + value: https://sparkyfitness.bstein.dev + - name: SPARKY_FITNESS_DISABLE_SIGNUP + value: "false" + - name: SPARKY_FITNESS_DB_HOST + valueFrom: + secretKeyRef: + name: sparkyfitness-db + key: SPARKY_FITNESS_DB_HOST + - name: SPARKY_FITNESS_DB_PORT + valueFrom: + secretKeyRef: + name: sparkyfitness-db + key: SPARKY_FITNESS_DB_PORT + - name: SPARKY_FITNESS_DB_NAME + valueFrom: + secretKeyRef: + name: sparkyfitness-db + key: SPARKY_FITNESS_DB_NAME + - name: SPARKY_FITNESS_DB_USER + valueFrom: + secretKeyRef: + name: sparkyfitness-db + key: SPARKY_FITNESS_DB_USER + - name: SPARKY_FITNESS_DB_PASSWORD + valueFrom: + secretKeyRef: + name: sparkyfitness-db + key: SPARKY_FITNESS_DB_PASSWORD + - name: SPARKY_FITNESS_APP_DB_USER + valueFrom: + secretKeyRef: + name: sparkyfitness-db + key: SPARKY_FITNESS_APP_DB_USER + - name: SPARKY_FITNESS_APP_DB_PASSWORD + valueFrom: + secretKeyRef: + name: sparkyfitness-db + key: SPARKY_FITNESS_APP_DB_PASSWORD + - name: SPARKY_FITNESS_API_ENCRYPTION_KEY + valueFrom: + secretKeyRef: + name: sparkyfitness-secrets + key: SPARKY_FITNESS_API_ENCRYPTION_KEY + - name: JWT_SECRET + valueFrom: + secretKeyRef: + name: sparkyfitness-secrets + key: JWT_SECRET + - name: SPARKY_FITNESS_ADMIN_EMAIL + valueFrom: + secretKeyRef: + name: sparkyfitness-admin + key: email + volumeMounts: + - name: sparkyfitness-data + mountPath: /app/SparkyFitnessServer/uploads + subPath: uploads + - name: sparkyfitness-data + mountPath: /app/SparkyFitnessServer/backup + subPath: backup + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 15 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 6 + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 30 + periodSeconds: 20 + timeoutSeconds: 3 + failureThreshold: 6 + resources: + requests: + cpu: 200m + memory: 512Mi + limits: + cpu: "1" + memory: 2Gi + volumes: + - name: sparkyfitness-data + persistentVolumeClaim: + claimName: sparkyfitness-data diff --git a/services/health/sparkyfitness-server-service.yaml b/services/health/sparkyfitness-server-service.yaml new file mode 100644 index 0000000..91220f9 --- /dev/null +++ b/services/health/sparkyfitness-server-service.yaml @@ -0,0 +1,15 @@ +# services/health/sparkyfitness-server-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: sparkyfitness-server + namespace: health + labels: + app: sparkyfitness-server +spec: + selector: + app: sparkyfitness-server + ports: + - name: http + port: 3010 + targetPort: http diff --git a/services/health/vault-sync-deployment.yaml b/services/health/vault-sync-deployment.yaml new file mode 100644 index 0000000..7b4c08e --- /dev/null +++ b/services/health/vault-sync-deployment.yaml @@ -0,0 +1,34 @@ +# services/health/vault-sync-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: health-vault-sync + namespace: health +spec: + replicas: 1 + selector: + matchLabels: + app: health-vault-sync + template: + metadata: + labels: + app: health-vault-sync + spec: + serviceAccountName: health-vault-sync + containers: + - name: sync + image: alpine:3.20 + command: ["/bin/sh", "-c"] + args: + - "sleep infinity" + volumeMounts: + - name: vault-secrets + mountPath: /vault/secrets + readOnly: true + volumes: + - name: vault-secrets + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: health-vault diff --git a/services/keycloak/deployment.yaml b/services/keycloak/deployment.yaml index b2842b1..6f6fd2b 100644 --- a/services/keycloak/deployment.yaml +++ b/services/keycloak/deployment.yaml @@ -25,58 +25,43 @@ spec: vault.hashicorp.com/role: "sso" vault.hashicorp.com/agent-inject-secret-keycloak-env.sh: "kv/data/atlas/shared/keycloak-admin" vault.hashicorp.com/agent-inject-template-keycloak-env.sh: | - {{- with secret "kv/data/atlas/shared/keycloak-admin" -}} + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KEYCLOAK_ADMIN="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_USER="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_PASSWORD="{{ .Data.data.password }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/keycloak-db" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/keycloak-db" }} export KC_DB_URL_DATABASE="{{ .Data.data.POSTGRES_DATABASE }}" export KC_DB_USERNAME="{{ .Data.data.POSTGRES_USER }}" export KC_DB_PASSWORD="{{ .Data.data.POSTGRES_PASSWORD }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/portal-e2e-client" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/portal-e2e-client" }} export PORTAL_E2E_CLIENT_ID="{{ .Data.data.client_id }}" export PORTAL_E2E_CLIENT_SECRET="{{ .Data.data.client_secret }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/openldap-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/openldap-admin" }} export LDAP_ADMIN_PASSWORD="{{ .Data.data.LDAP_ADMIN_PASSWORD }}" export LDAP_CONFIG_PASSWORD="{{ .Data.data.LDAP_CONFIG_PASSWORD }}" export LDAP_BIND_PASSWORD="${LDAP_ADMIN_PASSWORD}" - {{- end }} - {{- with secret "kv/data/atlas/shared/postmark-relay" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/postmark-relay" }} export KEYCLOAK_SMTP_USER="{{ index .Data.data "relay-username" }}" export KEYCLOAK_SMTP_PASSWORD="{{ index .Data.data "relay-password" }}" - {{- end }} + {{ end }} spec: serviceAccountName: sso-vault + nodeSelector: + kubernetes.io/arch: amd64 + node-role.kubernetes.io/accelerator: "true" affinity: nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: hardware - operator: In - values: ["rpi5","rpi4"] - - key: node-role.kubernetes.io/worker - operator: Exists - - matchExpressions: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + preference: + matchExpressions: - key: kubernetes.io/hostname operator: In - values: ["titan-24"] - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 90 - preference: - matchExpressions: - - key: hardware - operator: In - values: ["rpi5"] - - weight: 70 - preference: - matchExpressions: - - key: hardware - operator: In - values: ["rpi4"] + values: ["titan-22"] securityContext: runAsUser: 1000 runAsGroup: 0 diff --git a/services/keycloak/endurain-oidc-secret-ensure-job.yaml b/services/keycloak/endurain-oidc-secret-ensure-job.yaml new file mode 100644 index 0000000..9870f1d --- /dev/null +++ b/services/keycloak/endurain-oidc-secret-ensure-job.yaml @@ -0,0 +1,52 @@ +# services/keycloak/endurain-oidc-secret-ensure-job.yaml +apiVersion: batch/v1 +kind: Job +metadata: + name: endurain-oidc-secret-ensure-1 + namespace: sso +spec: + backoffLimit: 0 + ttlSecondsAfterFinished: 3600 + template: + metadata: + annotations: + vault.hashicorp.com/agent-inject: "true" + vault.hashicorp.com/role: "sso-secrets" + vault.hashicorp.com/agent-inject-secret-keycloak-admin-env.sh: "kv/data/atlas/shared/keycloak-admin" + vault.hashicorp.com/agent-inject-template-keycloak-admin-env.sh: | + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} + export KEYCLOAK_ADMIN="{{ .Data.data.username }}" + export KEYCLOAK_ADMIN_USER="{{ .Data.data.username }}" + export KEYCLOAK_ADMIN_PASSWORD="{{ .Data.data.password }}" + {{ end }} + spec: + serviceAccountName: mas-secrets-ensure + restartPolicy: Never + volumes: + - name: endurain-oidc-secret-ensure-script + configMap: + name: endurain-oidc-secret-ensure-script + defaultMode: 0555 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/arch + operator: In + values: ["arm64"] + - key: node-role.kubernetes.io/worker + operator: Exists + containers: + - name: apply + image: alpine:3.20 + command: ["/bin/sh", "-c"] + args: + - | + set -euo pipefail + apk add --no-cache bash curl jq >/dev/null + exec /scripts/endurain_oidc_secret_ensure.sh + volumeMounts: + - name: endurain-oidc-secret-ensure-script + mountPath: /scripts + readOnly: true diff --git a/services/keycloak/harbor-oidc-secret-ensure-job.yaml b/services/keycloak/harbor-oidc-secret-ensure-job.yaml index aa51f4a..999cb64 100644 --- a/services/keycloak/harbor-oidc-secret-ensure-job.yaml +++ b/services/keycloak/harbor-oidc-secret-ensure-job.yaml @@ -14,11 +14,11 @@ spec: vault.hashicorp.com/role: "sso-secrets" vault.hashicorp.com/agent-inject-secret-keycloak-admin-env.sh: "kv/data/atlas/shared/keycloak-admin" vault.hashicorp.com/agent-inject-template-keycloak-admin-env.sh: | - {{- with secret "kv/data/atlas/shared/keycloak-admin" -}} + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KEYCLOAK_ADMIN="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_USER="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_PASSWORD="{{ .Data.data.password }}" - {{- end }} + {{ end }} spec: serviceAccountName: mas-secrets-ensure restartPolicy: Never diff --git a/services/keycloak/kustomization.yaml b/services/keycloak/kustomization.yaml index e141467..6eb2691 100644 --- a/services/keycloak/kustomization.yaml +++ b/services/keycloak/kustomization.yaml @@ -22,6 +22,8 @@ resources: - synapse-oidc-secret-ensure-job.yaml - logs-oidc-secret-ensure-job.yaml - harbor-oidc-secret-ensure-job.yaml + - endurain-oidc-secret-ensure-job.yaml + - sparkyfitness-oidc-secret-ensure-job.yaml - vault-oidc-secret-ensure-job.yaml - service.yaml - ingress.yaml @@ -35,6 +37,12 @@ configMapGenerator: - name: harbor-oidc-secret-ensure-script files: - harbor_oidc_secret_ensure.sh=scripts/harbor_oidc_secret_ensure.sh + - name: endurain-oidc-secret-ensure-script + files: + - endurain_oidc_secret_ensure.sh=scripts/endurain_oidc_secret_ensure.sh + - name: sparkyfitness-oidc-secret-ensure-script + files: + - sparkyfitness_oidc_secret_ensure.sh=scripts/sparkyfitness_oidc_secret_ensure.sh - name: vault-oidc-secret-ensure-script files: - vault_oidc_secret_ensure.sh=scripts/vault_oidc_secret_ensure.sh diff --git a/services/keycloak/ldap-federation-job.yaml b/services/keycloak/ldap-federation-job.yaml index 68ce057..1d3d09e 100644 --- a/services/keycloak/ldap-federation-job.yaml +++ b/services/keycloak/ldap-federation-job.yaml @@ -13,29 +13,29 @@ spec: vault.hashicorp.com/role: "sso" vault.hashicorp.com/agent-inject-secret-keycloak-env.sh: "kv/data/atlas/shared/keycloak-admin" vault.hashicorp.com/agent-inject-template-keycloak-env.sh: | - {{- with secret "kv/data/atlas/shared/keycloak-admin" -}} + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KEYCLOAK_ADMIN="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_USER="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_PASSWORD="{{ .Data.data.password }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/keycloak-db" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/keycloak-db" }} export KC_DB_URL_DATABASE="{{ .Data.data.POSTGRES_DATABASE }}" export KC_DB_USERNAME="{{ .Data.data.POSTGRES_USER }}" export KC_DB_PASSWORD="{{ .Data.data.POSTGRES_PASSWORD }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/portal-e2e-client" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/portal-e2e-client" }} export PORTAL_E2E_CLIENT_ID="{{ .Data.data.client_id }}" export PORTAL_E2E_CLIENT_SECRET="{{ .Data.data.client_secret }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/openldap-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/openldap-admin" }} export LDAP_ADMIN_PASSWORD="{{ .Data.data.LDAP_ADMIN_PASSWORD }}" export LDAP_CONFIG_PASSWORD="{{ .Data.data.LDAP_CONFIG_PASSWORD }}" export LDAP_BIND_PASSWORD="${LDAP_ADMIN_PASSWORD}" - {{- end }} - {{- with secret "kv/data/atlas/shared/postmark-relay" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/postmark-relay" }} export KEYCLOAK_SMTP_USER="{{ index .Data.data "relay-username" }}" export KEYCLOAK_SMTP_PASSWORD="{{ index .Data.data "relay-password" }}" - {{- end }} + {{ end }} spec: affinity: nodeAffinity: diff --git a/services/keycloak/logs-oidc-secret-ensure-job.yaml b/services/keycloak/logs-oidc-secret-ensure-job.yaml index 7fc3097..cd6cc62 100644 --- a/services/keycloak/logs-oidc-secret-ensure-job.yaml +++ b/services/keycloak/logs-oidc-secret-ensure-job.yaml @@ -14,11 +14,11 @@ spec: vault.hashicorp.com/role: "sso-secrets" vault.hashicorp.com/agent-inject-secret-keycloak-admin-env.sh: "kv/data/atlas/shared/keycloak-admin" vault.hashicorp.com/agent-inject-template-keycloak-admin-env.sh: | - {{- with secret "kv/data/atlas/shared/keycloak-admin" -}} + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KEYCLOAK_ADMIN="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_USER="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_PASSWORD="{{ .Data.data.password }}" - {{- end }} + {{ end }} spec: serviceAccountName: mas-secrets-ensure restartPolicy: Never diff --git a/services/keycloak/mas-secrets-ensure-job.yaml b/services/keycloak/mas-secrets-ensure-job.yaml index 3b6e15e..4ad24e5 100644 --- a/services/keycloak/mas-secrets-ensure-job.yaml +++ b/services/keycloak/mas-secrets-ensure-job.yaml @@ -23,11 +23,11 @@ spec: vault.hashicorp.com/role: "sso-secrets" vault.hashicorp.com/agent-inject-secret-keycloak-admin-env.sh: "kv/data/atlas/shared/keycloak-admin" vault.hashicorp.com/agent-inject-template-keycloak-admin-env.sh: | - {{- with secret "kv/data/atlas/shared/keycloak-admin" -}} + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KEYCLOAK_ADMIN="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_USER="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_PASSWORD="{{ .Data.data.password }}" - {{- end }} + {{ end }} spec: serviceAccountName: mas-secrets-ensure restartPolicy: Never diff --git a/services/keycloak/portal-e2e-client-job.yaml b/services/keycloak/portal-e2e-client-job.yaml index 2cb50ca..7801690 100644 --- a/services/keycloak/portal-e2e-client-job.yaml +++ b/services/keycloak/portal-e2e-client-job.yaml @@ -13,29 +13,29 @@ spec: vault.hashicorp.com/role: "sso" vault.hashicorp.com/agent-inject-secret-keycloak-env.sh: "kv/data/atlas/shared/keycloak-admin" vault.hashicorp.com/agent-inject-template-keycloak-env.sh: | - {{- with secret "kv/data/atlas/shared/keycloak-admin" -}} + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KEYCLOAK_ADMIN="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_USER="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_PASSWORD="{{ .Data.data.password }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/keycloak-db" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/keycloak-db" }} export KC_DB_URL_DATABASE="{{ .Data.data.POSTGRES_DATABASE }}" export KC_DB_USERNAME="{{ .Data.data.POSTGRES_USER }}" export KC_DB_PASSWORD="{{ .Data.data.POSTGRES_PASSWORD }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/portal-e2e-client" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/portal-e2e-client" }} export PORTAL_E2E_CLIENT_ID="{{ .Data.data.client_id }}" export PORTAL_E2E_CLIENT_SECRET="{{ .Data.data.client_secret }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/openldap-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/openldap-admin" }} export LDAP_ADMIN_PASSWORD="{{ .Data.data.LDAP_ADMIN_PASSWORD }}" export LDAP_CONFIG_PASSWORD="{{ .Data.data.LDAP_CONFIG_PASSWORD }}" export LDAP_BIND_PASSWORD="${LDAP_ADMIN_PASSWORD}" - {{- end }} - {{- with secret "kv/data/atlas/shared/postmark-relay" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/postmark-relay" }} export KEYCLOAK_SMTP_USER="{{ index .Data.data "relay-username" }}" export KEYCLOAK_SMTP_PASSWORD="{{ index .Data.data "relay-password" }}" - {{- end }} + {{ end }} spec: restartPolicy: Never serviceAccountName: sso-vault diff --git a/services/keycloak/portal-e2e-execute-actions-email-test-job.yaml b/services/keycloak/portal-e2e-execute-actions-email-test-job.yaml index c80e3eb..5bf70ff 100644 --- a/services/keycloak/portal-e2e-execute-actions-email-test-job.yaml +++ b/services/keycloak/portal-e2e-execute-actions-email-test-job.yaml @@ -13,29 +13,29 @@ spec: vault.hashicorp.com/role: "sso" vault.hashicorp.com/agent-inject-secret-keycloak-env.sh: "kv/data/atlas/shared/keycloak-admin" vault.hashicorp.com/agent-inject-template-keycloak-env.sh: | - {{- with secret "kv/data/atlas/shared/keycloak-admin" -}} + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KEYCLOAK_ADMIN="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_USER="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_PASSWORD="{{ .Data.data.password }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/keycloak-db" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/keycloak-db" }} export KC_DB_URL_DATABASE="{{ .Data.data.POSTGRES_DATABASE }}" export KC_DB_USERNAME="{{ .Data.data.POSTGRES_USER }}" export KC_DB_PASSWORD="{{ .Data.data.POSTGRES_PASSWORD }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/portal-e2e-client" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/portal-e2e-client" }} export PORTAL_E2E_CLIENT_ID="{{ .Data.data.client_id }}" export PORTAL_E2E_CLIENT_SECRET="{{ .Data.data.client_secret }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/openldap-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/openldap-admin" }} export LDAP_ADMIN_PASSWORD="{{ .Data.data.LDAP_ADMIN_PASSWORD }}" export LDAP_CONFIG_PASSWORD="{{ .Data.data.LDAP_CONFIG_PASSWORD }}" export LDAP_BIND_PASSWORD="${LDAP_ADMIN_PASSWORD}" - {{- end }} - {{- with secret "kv/data/atlas/shared/postmark-relay" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/postmark-relay" }} export KEYCLOAK_SMTP_USER="{{ index .Data.data "relay-username" }}" export KEYCLOAK_SMTP_PASSWORD="{{ index .Data.data "relay-password" }}" - {{- end }} + {{ end }} spec: restartPolicy: Never serviceAccountName: sso-vault diff --git a/services/keycloak/portal-e2e-target-client-job.yaml b/services/keycloak/portal-e2e-target-client-job.yaml index c4dcd0f..68779bd 100644 --- a/services/keycloak/portal-e2e-target-client-job.yaml +++ b/services/keycloak/portal-e2e-target-client-job.yaml @@ -13,29 +13,29 @@ spec: vault.hashicorp.com/role: "sso" vault.hashicorp.com/agent-inject-secret-keycloak-env.sh: "kv/data/atlas/shared/keycloak-admin" vault.hashicorp.com/agent-inject-template-keycloak-env.sh: | - {{- with secret "kv/data/atlas/shared/keycloak-admin" -}} + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KEYCLOAK_ADMIN="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_USER="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_PASSWORD="{{ .Data.data.password }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/keycloak-db" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/keycloak-db" }} export KC_DB_URL_DATABASE="{{ .Data.data.POSTGRES_DATABASE }}" export KC_DB_USERNAME="{{ .Data.data.POSTGRES_USER }}" export KC_DB_PASSWORD="{{ .Data.data.POSTGRES_PASSWORD }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/portal-e2e-client" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/portal-e2e-client" }} export PORTAL_E2E_CLIENT_ID="{{ .Data.data.client_id }}" export PORTAL_E2E_CLIENT_SECRET="{{ .Data.data.client_secret }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/openldap-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/openldap-admin" }} export LDAP_ADMIN_PASSWORD="{{ .Data.data.LDAP_ADMIN_PASSWORD }}" export LDAP_CONFIG_PASSWORD="{{ .Data.data.LDAP_CONFIG_PASSWORD }}" export LDAP_BIND_PASSWORD="${LDAP_ADMIN_PASSWORD}" - {{- end }} - {{- with secret "kv/data/atlas/shared/postmark-relay" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/postmark-relay" }} export KEYCLOAK_SMTP_USER="{{ index .Data.data "relay-username" }}" export KEYCLOAK_SMTP_PASSWORD="{{ index .Data.data "relay-password" }}" - {{- end }} + {{ end }} spec: restartPolicy: Never serviceAccountName: sso-vault diff --git a/services/keycloak/portal-e2e-token-exchange-permissions-job.yaml b/services/keycloak/portal-e2e-token-exchange-permissions-job.yaml index cbd21ac..59a89f7 100644 --- a/services/keycloak/portal-e2e-token-exchange-permissions-job.yaml +++ b/services/keycloak/portal-e2e-token-exchange-permissions-job.yaml @@ -13,29 +13,29 @@ spec: vault.hashicorp.com/role: "sso" vault.hashicorp.com/agent-inject-secret-keycloak-env.sh: "kv/data/atlas/shared/keycloak-admin" vault.hashicorp.com/agent-inject-template-keycloak-env.sh: | - {{- with secret "kv/data/atlas/shared/keycloak-admin" -}} + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KEYCLOAK_ADMIN="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_USER="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_PASSWORD="{{ .Data.data.password }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/keycloak-db" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/keycloak-db" }} export KC_DB_URL_DATABASE="{{ .Data.data.POSTGRES_DATABASE }}" export KC_DB_USERNAME="{{ .Data.data.POSTGRES_USER }}" export KC_DB_PASSWORD="{{ .Data.data.POSTGRES_PASSWORD }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/portal-e2e-client" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/portal-e2e-client" }} export PORTAL_E2E_CLIENT_ID="{{ .Data.data.client_id }}" export PORTAL_E2E_CLIENT_SECRET="{{ .Data.data.client_secret }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/openldap-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/openldap-admin" }} export LDAP_ADMIN_PASSWORD="{{ .Data.data.LDAP_ADMIN_PASSWORD }}" export LDAP_CONFIG_PASSWORD="{{ .Data.data.LDAP_CONFIG_PASSWORD }}" export LDAP_BIND_PASSWORD="${LDAP_ADMIN_PASSWORD}" - {{- end }} - {{- with secret "kv/data/atlas/shared/postmark-relay" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/postmark-relay" }} export KEYCLOAK_SMTP_USER="{{ index .Data.data "relay-username" }}" export KEYCLOAK_SMTP_PASSWORD="{{ index .Data.data "relay-password" }}" - {{- end }} + {{ end }} spec: restartPolicy: Never serviceAccountName: sso-vault diff --git a/services/keycloak/portal-e2e-token-exchange-test-job.yaml b/services/keycloak/portal-e2e-token-exchange-test-job.yaml index 56c7ce5..a50a635 100644 --- a/services/keycloak/portal-e2e-token-exchange-test-job.yaml +++ b/services/keycloak/portal-e2e-token-exchange-test-job.yaml @@ -14,29 +14,29 @@ spec: vault.hashicorp.com/role: "sso" vault.hashicorp.com/agent-inject-secret-keycloak-env.sh: "kv/data/atlas/shared/keycloak-admin" vault.hashicorp.com/agent-inject-template-keycloak-env.sh: | - {{- with secret "kv/data/atlas/shared/keycloak-admin" -}} + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KEYCLOAK_ADMIN="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_USER="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_PASSWORD="{{ .Data.data.password }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/keycloak-db" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/keycloak-db" }} export KC_DB_URL_DATABASE="{{ .Data.data.POSTGRES_DATABASE }}" export KC_DB_USERNAME="{{ .Data.data.POSTGRES_USER }}" export KC_DB_PASSWORD="{{ .Data.data.POSTGRES_PASSWORD }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/portal-e2e-client" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/portal-e2e-client" }} export PORTAL_E2E_CLIENT_ID="{{ .Data.data.client_id }}" export PORTAL_E2E_CLIENT_SECRET="{{ .Data.data.client_secret }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/openldap-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/openldap-admin" }} export LDAP_ADMIN_PASSWORD="{{ .Data.data.LDAP_ADMIN_PASSWORD }}" export LDAP_CONFIG_PASSWORD="{{ .Data.data.LDAP_CONFIG_PASSWORD }}" export LDAP_BIND_PASSWORD="${LDAP_ADMIN_PASSWORD}" - {{- end }} - {{- with secret "kv/data/atlas/shared/postmark-relay" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/postmark-relay" }} export KEYCLOAK_SMTP_USER="{{ index .Data.data "relay-username" }}" export KEYCLOAK_SMTP_PASSWORD="{{ index .Data.data "relay-password" }}" - {{- end }} + {{ end }} spec: restartPolicy: Never serviceAccountName: sso-vault diff --git a/services/keycloak/realm-settings-job.yaml b/services/keycloak/realm-settings-job.yaml index f44dcd4..e650c30 100644 --- a/services/keycloak/realm-settings-job.yaml +++ b/services/keycloak/realm-settings-job.yaml @@ -13,29 +13,29 @@ spec: vault.hashicorp.com/role: "sso" vault.hashicorp.com/agent-inject-secret-keycloak-env.sh: "kv/data/atlas/shared/keycloak-admin" vault.hashicorp.com/agent-inject-template-keycloak-env.sh: | - {{- with secret "kv/data/atlas/shared/keycloak-admin" -}} + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KEYCLOAK_ADMIN="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_USER="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_PASSWORD="{{ .Data.data.password }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/keycloak-db" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/keycloak-db" }} export KC_DB_URL_DATABASE="{{ .Data.data.POSTGRES_DATABASE }}" export KC_DB_USERNAME="{{ .Data.data.POSTGRES_USER }}" export KC_DB_PASSWORD="{{ .Data.data.POSTGRES_PASSWORD }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/portal-e2e-client" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/portal-e2e-client" }} export PORTAL_E2E_CLIENT_ID="{{ .Data.data.client_id }}" export PORTAL_E2E_CLIENT_SECRET="{{ .Data.data.client_secret }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/openldap-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/openldap-admin" }} export LDAP_ADMIN_PASSWORD="{{ .Data.data.LDAP_ADMIN_PASSWORD }}" export LDAP_CONFIG_PASSWORD="{{ .Data.data.LDAP_CONFIG_PASSWORD }}" export LDAP_BIND_PASSWORD="${LDAP_ADMIN_PASSWORD}" - {{- end }} - {{- with secret "kv/data/atlas/shared/postmark-relay" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/postmark-relay" }} export KEYCLOAK_SMTP_USER="{{ index .Data.data "relay-username" }}" export KEYCLOAK_SMTP_PASSWORD="{{ index .Data.data "relay-password" }}" - {{- end }} + {{ end }} spec: affinity: nodeAffinity: diff --git a/services/keycloak/scripts/endurain_oidc_secret_ensure.sh b/services/keycloak/scripts/endurain_oidc_secret_ensure.sh new file mode 100644 index 0000000..6b026b0 --- /dev/null +++ b/services/keycloak/scripts/endurain_oidc_secret_ensure.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +set -euo pipefail + +. /vault/secrets/keycloak-admin-env.sh + +KC_URL="http://keycloak.sso.svc.cluster.local" +REALM="atlas" +CLIENT_ID="endurain" +ROOT_URL="https://endurain.bstein.dev" +REDIRECT_URI="https://endurain.bstein.dev/api/v1/public/idp/callback/keycloak" +ISSUER_URL="https://sso.bstein.dev/realms/atlas" + +ACCESS_TOKEN="" +for attempt in 1 2 3 4 5; do + TOKEN_JSON="$(curl -sS -X POST "$KC_URL/realms/master/protocol/openid-connect/token" \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + -d "grant_type=password" \ + -d "client_id=admin-cli" \ + -d "username=${KEYCLOAK_ADMIN}" \ + -d "password=${KEYCLOAK_ADMIN_PASSWORD}" || true)" + ACCESS_TOKEN="$(echo "$TOKEN_JSON" | jq -r '.access_token' 2>/dev/null || true)" + if [ -n "$ACCESS_TOKEN" ] && [ "$ACCESS_TOKEN" != "null" ]; then + break + fi + echo "Keycloak token request failed (attempt ${attempt})" >&2 + sleep $((attempt * 2)) +done +if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then + echo "Failed to fetch Keycloak admin token" >&2 + exit 1 +fi + +CLIENT_QUERY="$(curl -sS -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + "$KC_URL/admin/realms/${REALM}/clients?clientId=${CLIENT_ID}" || true)" +CLIENT_UUID="$(echo "$CLIENT_QUERY" | jq -r '.[0].id' 2>/dev/null || true)" + +if [ -z "$CLIENT_UUID" ] || [ "$CLIENT_UUID" = "null" ]; then + create_payload="$(jq -nc \ + --arg client_id "${CLIENT_ID}" \ + --arg root_url "${ROOT_URL}" \ + --arg redirect_uri "${REDIRECT_URI}" \ + --arg web_origin "${ROOT_URL}" \ + '{clientId:$client_id,name:"Endurain",enabled:true,protocol:"openid-connect",publicClient:false,standardFlowEnabled:true,implicitFlowEnabled:false,directAccessGrantsEnabled:false,serviceAccountsEnabled:false,redirectUris:[$redirect_uri],webOrigins:[$web_origin],rootUrl:$root_url,baseUrl:"/"}')" + status="$(curl -sS -o /dev/null -w "%{http_code}" -X POST \ + -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + -H 'Content-Type: application/json' \ + -d "${create_payload}" \ + "$KC_URL/admin/realms/${REALM}/clients")" + if [ "$status" != "201" ] && [ "$status" != "204" ]; then + echo "Keycloak client create failed (status ${status})" >&2 + exit 1 + fi + CLIENT_QUERY="$(curl -sS -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + "$KC_URL/admin/realms/${REALM}/clients?clientId=${CLIENT_ID}" || true)" + CLIENT_UUID="$(echo "$CLIENT_QUERY" | jq -r '.[0].id' 2>/dev/null || true)" +fi + +if [ -z "$CLIENT_UUID" ] || [ "$CLIENT_UUID" = "null" ]; then + echo "Keycloak client ${CLIENT_ID} not found" >&2 + exit 1 +fi + +CLIENT_SECRET="$(curl -sS -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + "$KC_URL/admin/realms/${REALM}/clients/${CLIENT_UUID}/client-secret" | jq -r '.value' 2>/dev/null || true)" +if [ -z "$CLIENT_SECRET" ] || [ "$CLIENT_SECRET" = "null" ]; then + echo "Keycloak client secret not found" >&2 + exit 1 +fi + +vault_addr="${VAULT_ADDR:-http://vault.vault.svc.cluster.local:8200}" +vault_role="${VAULT_ROLE:-sso-secrets}" +jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" +login_payload="$(jq -nc --arg jwt "${jwt}" --arg role "${vault_role}" '{jwt:$jwt, role:$role}')" +vault_token="$(curl -sS --request POST --data "${login_payload}" \ + "${vault_addr}/v1/auth/kubernetes/login" | jq -r '.auth.client_token')" +if [ -z "${vault_token}" ] || [ "${vault_token}" = "null" ]; then + echo "vault login failed" >&2 + exit 1 +fi + +payload="$(jq -nc \ + --arg client_id "${CLIENT_ID}" \ + --arg client_secret "${CLIENT_SECRET}" \ + --arg issuer_url "${ISSUER_URL}" \ + '{data:{client_id:$client_id,client_secret:$client_secret,issuer_url:$issuer_url}}')" +curl -sS -X POST -H "X-Vault-Token: ${vault_token}" \ + -d "${payload}" "${vault_addr}/v1/kv/data/atlas/health/endurain-oidc" >/dev/null diff --git a/services/keycloak/scripts/sparkyfitness_oidc_secret_ensure.sh b/services/keycloak/scripts/sparkyfitness_oidc_secret_ensure.sh new file mode 100644 index 0000000..449e81c --- /dev/null +++ b/services/keycloak/scripts/sparkyfitness_oidc_secret_ensure.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +set -euo pipefail + +. /vault/secrets/keycloak-admin-env.sh + +KC_URL="http://keycloak.sso.svc.cluster.local" +REALM="atlas" +CLIENT_ID="sparkyfitness" +ROOT_URL="https://sparkyfitness.bstein.dev" +REDIRECT_URI="https://sparkyfitness.bstein.dev/oidc-callback" +ISSUER_URL="https://sso.bstein.dev/realms/atlas" + +ACCESS_TOKEN="" +for attempt in 1 2 3 4 5; do + TOKEN_JSON="$(curl -sS -X POST "$KC_URL/realms/master/protocol/openid-connect/token" \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + -d "grant_type=password" \ + -d "client_id=admin-cli" \ + -d "username=${KEYCLOAK_ADMIN}" \ + -d "password=${KEYCLOAK_ADMIN_PASSWORD}" || true)" + ACCESS_TOKEN="$(echo "$TOKEN_JSON" | jq -r '.access_token' 2>/dev/null || true)" + if [ -n "$ACCESS_TOKEN" ] && [ "$ACCESS_TOKEN" != "null" ]; then + break + fi + echo "Keycloak token request failed (attempt ${attempt})" >&2 + sleep $((attempt * 2)) +done +if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then + echo "Failed to fetch Keycloak admin token" >&2 + exit 1 +fi + +CLIENT_QUERY="$(curl -sS -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + "$KC_URL/admin/realms/${REALM}/clients?clientId=${CLIENT_ID}" || true)" +CLIENT_UUID="$(echo "$CLIENT_QUERY" | jq -r '.[0].id' 2>/dev/null || true)" + +if [ -z "$CLIENT_UUID" ] || [ "$CLIENT_UUID" = "null" ]; then + create_payload="$(jq -nc \ + --arg client_id "${CLIENT_ID}" \ + --arg root_url "${ROOT_URL}" \ + --arg redirect_uri "${REDIRECT_URI}" \ + --arg web_origin "${ROOT_URL}" \ + '{clientId:$client_id,name:"SparkyFitness",enabled:true,protocol:"openid-connect",publicClient:false,standardFlowEnabled:true,implicitFlowEnabled:false,directAccessGrantsEnabled:false,serviceAccountsEnabled:false,redirectUris:[$redirect_uri],webOrigins:[$web_origin],rootUrl:$root_url,baseUrl:"/"}')" + status="$(curl -sS -o /dev/null -w "%{http_code}" -X POST \ + -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + -H 'Content-Type: application/json' \ + -d "${create_payload}" \ + "$KC_URL/admin/realms/${REALM}/clients")" + if [ "$status" != "201" ] && [ "$status" != "204" ]; then + echo "Keycloak client create failed (status ${status})" >&2 + exit 1 + fi + CLIENT_QUERY="$(curl -sS -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + "$KC_URL/admin/realms/${REALM}/clients?clientId=${CLIENT_ID}" || true)" + CLIENT_UUID="$(echo "$CLIENT_QUERY" | jq -r '.[0].id' 2>/dev/null || true)" +fi + +if [ -z "$CLIENT_UUID" ] || [ "$CLIENT_UUID" = "null" ]; then + echo "Keycloak client ${CLIENT_ID} not found" >&2 + exit 1 +fi + +CLIENT_SECRET="$(curl -sS -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + "$KC_URL/admin/realms/${REALM}/clients/${CLIENT_UUID}/client-secret" | jq -r '.value' 2>/dev/null || true)" +if [ -z "$CLIENT_SECRET" ] || [ "$CLIENT_SECRET" = "null" ]; then + echo "Keycloak client secret not found" >&2 + exit 1 +fi + +vault_addr="${VAULT_ADDR:-http://vault.vault.svc.cluster.local:8200}" +vault_role="${VAULT_ROLE:-sso-secrets}" +jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" +login_payload="$(jq -nc --arg jwt "${jwt}" --arg role "${vault_role}" '{jwt:$jwt, role:$role}')" +vault_token="$(curl -sS --request POST --data "${login_payload}" \ + "${vault_addr}/v1/auth/kubernetes/login" | jq -r '.auth.client_token')" +if [ -z "${vault_token}" ] || [ "${vault_token}" = "null" ]; then + echo "vault login failed" >&2 + exit 1 +fi + +payload="$(jq -nc \ + --arg client_id "${CLIENT_ID}" \ + --arg client_secret "${CLIENT_SECRET}" \ + --arg issuer_url "${ISSUER_URL}" \ + '{data:{client_id:$client_id,client_secret:$client_secret,issuer_url:$issuer_url}}')" +curl -sS -X POST -H "X-Vault-Token: ${vault_token}" \ + -d "${payload}" "${vault_addr}/v1/kv/data/atlas/health/sparkyfitness-oidc" >/dev/null diff --git a/services/keycloak/sparkyfitness-oidc-secret-ensure-job.yaml b/services/keycloak/sparkyfitness-oidc-secret-ensure-job.yaml new file mode 100644 index 0000000..17e6deb --- /dev/null +++ b/services/keycloak/sparkyfitness-oidc-secret-ensure-job.yaml @@ -0,0 +1,52 @@ +# services/keycloak/sparkyfitness-oidc-secret-ensure-job.yaml +apiVersion: batch/v1 +kind: Job +metadata: + name: sparkyfitness-oidc-secret-ensure-1 + namespace: sso +spec: + backoffLimit: 0 + ttlSecondsAfterFinished: 3600 + template: + metadata: + annotations: + vault.hashicorp.com/agent-inject: "true" + vault.hashicorp.com/role: "sso-secrets" + vault.hashicorp.com/agent-inject-secret-keycloak-admin-env.sh: "kv/data/atlas/shared/keycloak-admin" + vault.hashicorp.com/agent-inject-template-keycloak-admin-env.sh: | + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} + export KEYCLOAK_ADMIN="{{ .Data.data.username }}" + export KEYCLOAK_ADMIN_USER="{{ .Data.data.username }}" + export KEYCLOAK_ADMIN_PASSWORD="{{ .Data.data.password }}" + {{ end }} + spec: + serviceAccountName: mas-secrets-ensure + restartPolicy: Never + volumes: + - name: sparkyfitness-oidc-secret-ensure-script + configMap: + name: sparkyfitness-oidc-secret-ensure-script + defaultMode: 0555 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/arch + operator: In + values: ["arm64"] + - key: node-role.kubernetes.io/worker + operator: Exists + containers: + - name: apply + image: alpine:3.20 + command: ["/bin/sh", "-c"] + args: + - | + set -euo pipefail + apk add --no-cache bash curl jq >/dev/null + exec /scripts/sparkyfitness_oidc_secret_ensure.sh + volumeMounts: + - name: sparkyfitness-oidc-secret-ensure-script + mountPath: /scripts + readOnly: true diff --git a/services/keycloak/synapse-oidc-secret-ensure-job.yaml b/services/keycloak/synapse-oidc-secret-ensure-job.yaml index 1e4878d..712ca9d 100644 --- a/services/keycloak/synapse-oidc-secret-ensure-job.yaml +++ b/services/keycloak/synapse-oidc-secret-ensure-job.yaml @@ -14,11 +14,11 @@ spec: vault.hashicorp.com/role: "sso-secrets" vault.hashicorp.com/agent-inject-secret-keycloak-admin-env.sh: "kv/data/atlas/shared/keycloak-admin" vault.hashicorp.com/agent-inject-template-keycloak-admin-env.sh: | - {{- with secret "kv/data/atlas/shared/keycloak-admin" -}} + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KEYCLOAK_ADMIN="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_USER="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_PASSWORD="{{ .Data.data.password }}" - {{- end }} + {{ end }} spec: serviceAccountName: mas-secrets-ensure restartPolicy: Never diff --git a/services/keycloak/user-overrides-job.yaml b/services/keycloak/user-overrides-job.yaml index 495af18..7ca098a 100644 --- a/services/keycloak/user-overrides-job.yaml +++ b/services/keycloak/user-overrides-job.yaml @@ -13,29 +13,29 @@ spec: vault.hashicorp.com/role: "sso" vault.hashicorp.com/agent-inject-secret-keycloak-env.sh: "kv/data/atlas/shared/keycloak-admin" vault.hashicorp.com/agent-inject-template-keycloak-env.sh: | - {{- with secret "kv/data/atlas/shared/keycloak-admin" -}} + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KEYCLOAK_ADMIN="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_USER="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_PASSWORD="{{ .Data.data.password }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/keycloak-db" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/keycloak-db" }} export KC_DB_URL_DATABASE="{{ .Data.data.POSTGRES_DATABASE }}" export KC_DB_USERNAME="{{ .Data.data.POSTGRES_USER }}" export KC_DB_PASSWORD="{{ .Data.data.POSTGRES_PASSWORD }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/portal-e2e-client" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/portal-e2e-client" }} export PORTAL_E2E_CLIENT_ID="{{ .Data.data.client_id }}" export PORTAL_E2E_CLIENT_SECRET="{{ .Data.data.client_secret }}" - {{- end }} - {{- with secret "kv/data/atlas/sso/openldap-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/sso/openldap-admin" }} export LDAP_ADMIN_PASSWORD="{{ .Data.data.LDAP_ADMIN_PASSWORD }}" export LDAP_CONFIG_PASSWORD="{{ .Data.data.LDAP_CONFIG_PASSWORD }}" export LDAP_BIND_PASSWORD="${LDAP_ADMIN_PASSWORD}" - {{- end }} - {{- with secret "kv/data/atlas/shared/postmark-relay" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/postmark-relay" }} export KEYCLOAK_SMTP_USER="{{ index .Data.data "relay-username" }}" export KEYCLOAK_SMTP_PASSWORD="{{ index .Data.data "relay-password" }}" - {{- end }} + {{ end }} spec: affinity: nodeAffinity: diff --git a/services/keycloak/vault-oidc-secret-ensure-job.yaml b/services/keycloak/vault-oidc-secret-ensure-job.yaml index 797cada..31de281 100644 --- a/services/keycloak/vault-oidc-secret-ensure-job.yaml +++ b/services/keycloak/vault-oidc-secret-ensure-job.yaml @@ -14,11 +14,11 @@ spec: vault.hashicorp.com/role: "sso-secrets" vault.hashicorp.com/agent-inject-secret-keycloak-admin-env.sh: "kv/data/atlas/shared/keycloak-admin" vault.hashicorp.com/agent-inject-template-keycloak-admin-env.sh: | - {{- with secret "kv/data/atlas/shared/keycloak-admin" -}} + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KEYCLOAK_ADMIN="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_USER="{{ .Data.data.username }}" export KEYCLOAK_ADMIN_PASSWORD="{{ .Data.data.password }}" - {{- end }} + {{ end }} spec: serviceAccountName: mas-secrets-ensure restartPolicy: Never diff --git a/services/nextcloud-mail-sync/cronjob.yaml b/services/nextcloud-mail-sync/cronjob.yaml index 5042f4b..e6dcd37 100644 --- a/services/nextcloud-mail-sync/cronjob.yaml +++ b/services/nextcloud-mail-sync/cronjob.yaml @@ -18,29 +18,29 @@ spec: vault.hashicorp.com/role: "nextcloud" vault.hashicorp.com/agent-inject-secret-nextcloud-env.sh: "kv/data/atlas/nextcloud/nextcloud-db" vault.hashicorp.com/agent-inject-template-nextcloud-env.sh: | - {{- with secret "kv/data/atlas/nextcloud/nextcloud-db" -}} + {{ with secret "kv/data/atlas/nextcloud/nextcloud-db" }} export POSTGRES_DB="{{ .Data.data.database }}" export POSTGRES_USER="{{ index .Data.data "db-username" }}" export POSTGRES_PASSWORD="{{ index .Data.data "db-password" }}" - {{- end }} - {{- with secret "kv/data/atlas/nextcloud/nextcloud-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/nextcloud/nextcloud-admin" }} export NEXTCLOUD_ADMIN_USER="{{ index .Data.data "admin-user" }}" export NEXTCLOUD_ADMIN_PASSWORD="{{ index .Data.data "admin-password" }}" - {{- end }} + {{ end }} export ADMIN_USER="${NEXTCLOUD_ADMIN_USER}" export ADMIN_PASS="${NEXTCLOUD_ADMIN_PASSWORD}" - {{- with secret "kv/data/atlas/nextcloud/nextcloud-oidc" -}} + {{ with secret "kv/data/atlas/nextcloud/nextcloud-oidc" }} export OIDC_CLIENT_ID="{{ index .Data.data "client-id" }}" export OIDC_CLIENT_SECRET="{{ index .Data.data "client-secret" }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/postmark-relay" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/postmark-relay" }} export SMTP_NAME="{{ index .Data.data "relay-username" }}" export SMTP_PASSWORD="{{ index .Data.data "relay-password" }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/keycloak-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KC_ADMIN_USER="{{ .Data.data.username }}" export KC_ADMIN_PASS="{{ .Data.data.password }}" - {{- end }} + {{ end }} spec: restartPolicy: OnFailure securityContext: diff --git a/services/nextcloud/deployment.yaml b/services/nextcloud/deployment.yaml index 063c6f7..9af1e00 100644 --- a/services/nextcloud/deployment.yaml +++ b/services/nextcloud/deployment.yaml @@ -21,29 +21,29 @@ spec: vault.hashicorp.com/role: "nextcloud" vault.hashicorp.com/agent-inject-secret-nextcloud-env.sh: "kv/data/atlas/nextcloud/nextcloud-db" vault.hashicorp.com/agent-inject-template-nextcloud-env.sh: | - {{- with secret "kv/data/atlas/nextcloud/nextcloud-db" -}} + {{ with secret "kv/data/atlas/nextcloud/nextcloud-db" }} export POSTGRES_DB="{{ .Data.data.database }}" export POSTGRES_USER="{{ index .Data.data "db-username" }}" export POSTGRES_PASSWORD="{{ index .Data.data "db-password" }}" - {{- end }} - {{- with secret "kv/data/atlas/nextcloud/nextcloud-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/nextcloud/nextcloud-admin" }} export NEXTCLOUD_ADMIN_USER="{{ index .Data.data "admin-user" }}" export NEXTCLOUD_ADMIN_PASSWORD="{{ index .Data.data "admin-password" }}" - {{- end }} + {{ end }} export ADMIN_USER="${NEXTCLOUD_ADMIN_USER}" export ADMIN_PASS="${NEXTCLOUD_ADMIN_PASSWORD}" - {{- with secret "kv/data/atlas/nextcloud/nextcloud-oidc" -}} + {{ with secret "kv/data/atlas/nextcloud/nextcloud-oidc" }} export OIDC_CLIENT_ID="{{ index .Data.data "client-id" }}" export OIDC_CLIENT_SECRET="{{ index .Data.data "client-secret" }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/postmark-relay" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/postmark-relay" }} export SMTP_NAME="{{ index .Data.data "relay-username" }}" export SMTP_PASSWORD="{{ index .Data.data "relay-password" }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/keycloak-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KC_ADMIN_USER="{{ .Data.data.username }}" export KC_ADMIN_PASS="{{ .Data.data.password }}" - {{- end }} + {{ end }} spec: nodeSelector: hardware: rpi5 diff --git a/services/nextcloud/maintenance-cronjob.yaml b/services/nextcloud/maintenance-cronjob.yaml index f8af256..8c92417 100644 --- a/services/nextcloud/maintenance-cronjob.yaml +++ b/services/nextcloud/maintenance-cronjob.yaml @@ -16,29 +16,29 @@ spec: vault.hashicorp.com/role: "nextcloud" vault.hashicorp.com/agent-inject-secret-nextcloud-env.sh: "kv/data/atlas/nextcloud/nextcloud-db" vault.hashicorp.com/agent-inject-template-nextcloud-env.sh: | - {{- with secret "kv/data/atlas/nextcloud/nextcloud-db" -}} + {{ with secret "kv/data/atlas/nextcloud/nextcloud-db" }} export POSTGRES_DB="{{ .Data.data.database }}" export POSTGRES_USER="{{ index .Data.data "db-username" }}" export POSTGRES_PASSWORD="{{ index .Data.data "db-password" }}" - {{- end }} - {{- with secret "kv/data/atlas/nextcloud/nextcloud-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/nextcloud/nextcloud-admin" }} export NEXTCLOUD_ADMIN_USER="{{ index .Data.data "admin-user" }}" export NEXTCLOUD_ADMIN_PASSWORD="{{ index .Data.data "admin-password" }}" - {{- end }} + {{ end }} export ADMIN_USER="${NEXTCLOUD_ADMIN_USER}" export ADMIN_PASS="${NEXTCLOUD_ADMIN_PASSWORD}" - {{- with secret "kv/data/atlas/nextcloud/nextcloud-oidc" -}} + {{ with secret "kv/data/atlas/nextcloud/nextcloud-oidc" }} export OIDC_CLIENT_ID="{{ index .Data.data "client-id" }}" export OIDC_CLIENT_SECRET="{{ index .Data.data "client-secret" }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/postmark-relay" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/postmark-relay" }} export SMTP_NAME="{{ index .Data.data "relay-username" }}" export SMTP_PASSWORD="{{ index .Data.data "relay-password" }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/keycloak-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KC_ADMIN_USER="{{ .Data.data.username }}" export KC_ADMIN_PASS="{{ .Data.data.password }}" - {{- end }} + {{ end }} spec: restartPolicy: OnFailure securityContext: diff --git a/services/outline/deployment.yaml b/services/outline/deployment.yaml index 04341a0..471d185 100644 --- a/services/outline/deployment.yaml +++ b/services/outline/deployment.yaml @@ -25,29 +25,29 @@ spec: vault.hashicorp.com/role: "outline" vault.hashicorp.com/agent-inject-secret-outline-env.sh: "kv/data/atlas/outline/outline-db" vault.hashicorp.com/agent-inject-template-outline-env.sh: | - {{- with secret "kv/data/atlas/outline/outline-db" -}} + {{ with secret "kv/data/atlas/outline/outline-db" }} export DATABASE_URL="{{ .Data.data.DATABASE_URL }}" - {{- end }} - {{- with secret "kv/data/atlas/outline/outline-secrets" -}} + {{ end }} + {{ with secret "kv/data/atlas/outline/outline-secrets" }} export SECRET_KEY="{{ .Data.data.SECRET_KEY }}" export UTILS_SECRET="{{ .Data.data.UTILS_SECRET }}" - {{- end }} - {{- with secret "kv/data/atlas/outline/outline-oidc" -}} + {{ end }} + {{ with secret "kv/data/atlas/outline/outline-oidc" }} export OIDC_AUTH_URI="{{ .Data.data.OIDC_AUTH_URI }}" export OIDC_CLIENT_ID="{{ .Data.data.OIDC_CLIENT_ID }}" export OIDC_CLIENT_SECRET="{{ .Data.data.OIDC_CLIENT_SECRET }}" export OIDC_LOGOUT_URI="{{ .Data.data.OIDC_LOGOUT_URI }}" export OIDC_TOKEN_URI="{{ .Data.data.OIDC_TOKEN_URI }}" export OIDC_USERINFO_URI="{{ .Data.data.OIDC_USERINFO_URI }}" - {{- end }} - {{- with secret "kv/data/atlas/outline/outline-smtp" -}} + {{ end }} + {{ with secret "kv/data/atlas/outline/outline-smtp" }} export SMTP_FROM_EMAIL="{{ .Data.data.SMTP_FROM_EMAIL }}" export SMTP_HOST="{{ .Data.data.SMTP_HOST }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/postmark-relay" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/postmark-relay" }} export SMTP_USERNAME="{{ index .Data.data "relay-username" }}" export SMTP_PASSWORD="{{ index .Data.data "relay-password" }}" - {{- end }} + {{ end }} spec: serviceAccountName: outline-vault nodeSelector: diff --git a/services/planka/deployment.yaml b/services/planka/deployment.yaml index cec505f..7d8a628 100644 --- a/services/planka/deployment.yaml +++ b/services/planka/deployment.yaml @@ -25,13 +25,13 @@ spec: vault.hashicorp.com/role: "planka" vault.hashicorp.com/agent-inject-secret-planka-env.sh: "kv/data/atlas/planka/planka-db" vault.hashicorp.com/agent-inject-template-planka-env.sh: | - {{- with secret "kv/data/atlas/planka/planka-db" -}} + {{ with secret "kv/data/atlas/planka/planka-db" }} export DATABASE_URL="{{ .Data.data.DATABASE_URL }}" - {{- end }} - {{- with secret "kv/data/atlas/planka/planka-secrets" -}} + {{ end }} + {{ with secret "kv/data/atlas/planka/planka-secrets" }} export SECRET_KEY="{{ .Data.data.SECRET_KEY }}" - {{- end }} - {{- with secret "kv/data/atlas/planka/planka-oidc" -}} + {{ end }} + {{ with secret "kv/data/atlas/planka/planka-oidc" }} export OIDC_CLIENT_ID="{{ .Data.data.OIDC_CLIENT_ID }}" export OIDC_CLIENT_SECRET="{{ .Data.data.OIDC_CLIENT_SECRET }}" export OIDC_ENFORCED="{{ .Data.data.OIDC_ENFORCED }}" @@ -39,18 +39,18 @@ spec: export OIDC_ISSUER="{{ .Data.data.OIDC_ISSUER }}" export OIDC_SCOPES="{{ .Data.data.OIDC_SCOPES }}" export OIDC_USE_OAUTH_CALLBACK="{{ .Data.data.OIDC_USE_OAUTH_CALLBACK }}" - {{- end }} - {{- with secret "kv/data/atlas/planka/planka-smtp" -}} + {{ end }} + {{ with secret "kv/data/atlas/planka/planka-smtp" }} export SMTP_FROM="{{ .Data.data.SMTP_FROM }}" export SMTP_HOST="{{ .Data.data.SMTP_HOST }}" export SMTP_PORT="{{ .Data.data.SMTP_PORT }}" export SMTP_SECURE="{{ .Data.data.SMTP_SECURE }}" export SMTP_TLS_REJECT_UNAUTHORIZED="{{ .Data.data.SMTP_TLS_REJECT_UNAUTHORIZED }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/postmark-relay" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/postmark-relay" }} export SMTP_USER="{{ index .Data.data "relay-username" }}" export SMTP_PASSWORD="{{ index .Data.data "relay-password" }}" - {{- end }} + {{ end }} spec: serviceAccountName: planka-vault nodeSelector: diff --git a/services/vault/scripts/vault_k8s_auth_configure.sh b/services/vault/scripts/vault_k8s_auth_configure.sh index 3ecbd3f..f96dd94 100644 --- a/services/vault/scripts/vault_k8s_auth_configure.sh +++ b/services/vault/scripts/vault_k8s_auth_configure.sh @@ -105,10 +105,12 @@ write_policy_and_role "pegasus" "jellyfin" "pegasus-vault-sync" \ "pegasus/* harbor-pull/jellyfin" "" write_policy_and_role "crypto" "crypto" "crypto-vault-sync" \ "crypto/* harbor-pull/crypto" "" +write_policy_and_role "health" "health" "health-vault-sync" \ + "health/*" "" write_policy_and_role "sso-secrets" "sso" "mas-secrets-ensure" \ "shared/keycloak-admin" \ - "harbor/harbor-oidc vault/vault-oidc-config comms/synapse-oidc logging/oauth2-proxy-logs-oidc" + "harbor/harbor-oidc vault/vault-oidc-config comms/synapse-oidc logging/oauth2-proxy-logs-oidc health/endurain-oidc health/sparkyfitness-oidc" write_policy_and_role "comms-secrets" "comms" \ "comms-secrets-ensure,mas-db-ensure,mas-admin-client-secret-writer,othrys-synapse-signingkey-job" \ "" \ diff --git a/services/vaultwarden/deployment.yaml b/services/vaultwarden/deployment.yaml index 57789a7..2fde277 100644 --- a/services/vaultwarden/deployment.yaml +++ b/services/vaultwarden/deployment.yaml @@ -23,16 +23,16 @@ spec: vault.hashicorp.com/role: "vaultwarden" vault.hashicorp.com/agent-inject-secret-vaultwarden-env.sh: "kv/data/atlas/vaultwarden/vaultwarden-db-url" vault.hashicorp.com/agent-inject-template-vaultwarden-env.sh: | - {{- with secret "kv/data/atlas/vaultwarden/vaultwarden-db-url" -}} + {{ with secret "kv/data/atlas/vaultwarden/vaultwarden-db-url" }} export DATABASE_URL="{{ .Data.data.DATABASE_URL }}" - {{- end }} - {{- with secret "kv/data/atlas/vaultwarden/vaultwarden-admin" -}} + {{ end }} + {{ with secret "kv/data/atlas/vaultwarden/vaultwarden-admin" }} export ADMIN_TOKEN="{{ .Data.data.ADMIN_TOKEN }}" - {{- end }} - {{- with secret "kv/data/atlas/shared/postmark-relay" -}} + {{ end }} + {{ with secret "kv/data/atlas/shared/postmark-relay" }} export SMTP_USERNAME="{{ index .Data.data "relay-username" }}" export SMTP_PASSWORD="{{ index .Data.data "relay-password" }}" - {{- end }} + {{ end }} spec: serviceAccountName: vaultwarden-vault containers: