diff --git a/automation/dbildungs-iam-server/Chart.lock b/automation/dbildungs-iam-server/Chart.lock index e80a7aa31..4bf4219e0 100644 --- a/automation/dbildungs-iam-server/Chart.lock +++ b/automation/dbildungs-iam-server/Chart.lock @@ -3,4 +3,4 @@ dependencies: repository: https://charts.bitnami.com/bitnami version: 11.0.6 digest: sha256:790bafa04fe9c1cc9f772dc12fada16eb847c282f738fd23df09f665af93ec74 -generated: "2024-11-08T15:47:20.334302258Z" +generated: "2024-11-08T15:47:56.436299898Z" diff --git a/automation/dbildungs-iam-server/Chart.yaml b/automation/dbildungs-iam-server/Chart.yaml index 7df307c55..61caeab53 100644 --- a/automation/dbildungs-iam-server/Chart.yaml +++ b/automation/dbildungs-iam-server/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -appVersion: sha-665c83c +appVersion: DBP-1022 dependencies: - condition: redis-cluster.enabled name: redis-cluster @@ -8,4 +8,4 @@ dependencies: description: dBildungs-IAM-server name: dbildungs-iam-server type: application -version: 0.0.1-20241108-1547 +version: 0.0.0-dbp-1022-20241108-1547 diff --git a/automation/dbildungs-iam-server/cron/Dockerfile b/automation/dbildungs-iam-server/cron/Dockerfile new file mode 100644 index 000000000..8d2f652b5 --- /dev/null +++ b/automation/dbildungs-iam-server/cron/Dockerfile @@ -0,0 +1,24 @@ +FROM alpine:latest + +# Install necessary packages +RUN apk update && \ + apk add --no-cache bash openssl curl cronie jq vim + +# Copy scripts into the image +COPY scripts/ /scripts/ + +# Set execute permissions for all .sh scripts in /scripts/ +RUN chmod +x /scripts/*.sh + +# Copy crontab into the image +COPY crontab /etc/crontabs/root + +# Set correct permissions for crontab +RUN chmod 600 /etc/crontabs/root + +# Create a log file if your scripts write to a file +RUN touch /var/log/cron.log && \ + chmod 644 /var/log/cron.log + +# Start the cron service in foreground +CMD ["/usr/sbin/crond", "-f"] diff --git a/automation/dbildungs-iam-server/cron/crontab b/automation/dbildungs-iam-server/cron/crontab new file mode 100644 index 000000000..279bb8f83 --- /dev/null +++ b/automation/dbildungs-iam-server/cron/crontab @@ -0,0 +1 @@ +* * * * * /scripts/trigger_xyz.sh >> /var/log/cron.log diff --git a/automation/dbildungs-iam-server/cron/keys/dummy_jwks.json b/automation/dbildungs-iam-server/cron/keys/dummy_jwks.json new file mode 100644 index 000000000..9de15826d --- /dev/null +++ b/automation/dbildungs-iam-server/cron/keys/dummy_jwks.json @@ -0,0 +1,15 @@ +{ + "keys": [ + { + "kty": "RSA", + "n": "m7yAgb97yHR9AhKgeIQ2rV3aqOeUuUId9yETShol7F2A1W71E6Srt_iI1osfytCj1LKTLCSxFhMLvKXXAQBOj2VZMKa0xSYjoyy_7N6QqKBxtreeP8mW8IkEbd2ioqbbRvk-SuFmRI-nkgux2ZRdP5P9J8rBkQeAWADwayqsGSYJZ_Ok4D0CpR4mIXaXWVuHmqmjEegKNPoll-XTp2fR2X0u_fK0n2bm-0HdkG-wrlDrIZ4eaMYqZcE327_1AUxi6BIFymECZJzMIhpGoDbMwGIv60jkbqzlwqhbxSHcqv_6mexrAYHtm1v4iVM82WpQPR28eQFmHGlJdR0wCMDW6H-zN08PlCaRa6bRoR2ca_GRCvIVITioUqPzWLFAAe9VXvJ1cWOQVLK4oB40WoomDMgJDH5IcfRLNrGF4r-QskP34i04jobASQn6puBymkn-c-HJe1uEHxE6w22UVZqONses9SqYVaFP2AbptPQNuYktqrONKm-Plavo4ijPA-D2AE2ltQL7jfXK-XQGjSk7PxJ7prK6khLpbk2Snn4B5bgWrD8QTa784SM10fMMCzr6xKvjr5ZZYItTAID6kZ2k5x8cXUCede8IRvqweBdeIOYv7KIP4IvukxIbBlcfM59wF1sqtEiLwd5r2RARxscl1-_FF6i7kXTdY2F4N3nik70", + "e": "AQAB", + "d": "FUl4z0NbKhUyGRxurspzptLTVo8A9ymgau0sc86LLHnDaGkYOq0hg64HFjxCBjjqi-I0163UPs4vpoOMlehy1Irn3AyNXP1PlbhCK6BoaYQ0xuLmUJW_efVgk5vd5Rmlk1fyOc4BapGwN_g9QR5UeKJD0jKWrro1wtGuCxf0GiPm2TymZrgwKl2qnJxXJ7GpIWzai1IKQx-GD_MsMQ-x0WaZC8EnCzrHi8R_AoUhw1HwMdIpGe0sUmhMuE7OsHS-eDYucHE-IH6GMIsi6DdVWV6Saji2cKJVbXwRvypXvU_r8yornjMiRUAXAr5L7PPpL9Fmt6uHR47kqYtpy2iAd2SWNa6yvDzYGnV3N-Eq_7tRvsr8IiWVWqpH7NpDDMHseVobtm0gzLtgrUDhlGkGid0Mt2pCBnYMiECtiE4zcz0u9OifEcyUgbuKKdTZs1F24u6MCUJF-N2s6juNL-iE3cK3QAXPLmsXGy0rr3P7q5N9i_4x1Ha2XOSbyU_gb-EAaXEUTRgQdLSmj85H2UIudqwS63MYVTDpFmHZraVN-jxsBPISN6EAcRvBFbqUpq7rwHJrbQECKbuB12xm9iv4gpabUl_RaempTJNodDQNY5hc3i4e2Vr0FpY_yThbQ1vn444TexdSCJ1Sp2wjI10hwpr4zV5k-7KKDmqBEND8VAk", + "p": "yiMeDN349vQgClqAaUI2j2Wv_SUR9VW33Y6ftkMaxz67nsA0X621Y4e41V_npd3YRM5rQr4hTWfovn-Te1N3R57zmfF3z5RyzdKflNgQAe9cryNoa5s5AUP4MYnlkZE740UlNTV5VUeVp_3HoB83lvnVAT2mmKLa4cgBKzT6i97HtotBT6in7tdkeQQmqM4T9dX7G_9v9AjOwXJVqLJlxsVwplm-_2sBoFutlpBkcL46SeNIQm1aGvF2A7Vgfc1qlVG_PDu8UuxFZ_p_Onl52Cwx6-p_hAosiA44o6KJIet2jTa28hjT516nmDaO1juMGWPw-rDpx3VrD_mhRglk3w", + "q": "xTwhHr7PnBYHJq1F2DiTR_MrCh2x0lcc3x_9OAhrVGfvGUO2Rn5gFxTDzbFgIJx7-Anc4AzWghvD_4xBGX5nYKZgCo_kmZzwWpDtUVUl_0tDn7srgVXrRgodd9eVpuMUn8o2lGp0qoYqW4ZX_V2wGDxsQHYwxuXEl7KgweGaLkYTdCGmC0yW5GSuDDkcipurLqmvTbE8uDfORhvXPKieVohZUJtNfltX7rMBVZ0NKv0PCT4XTCwwknU7qi7_xb643NdVvWeIp2Q-exN9ZGu4qm-uR85nNZNPUw4_YStI1cmwAkcswQMT70ydWvoKJtaHJN7_fnpkP1TaS63uVcQe4w", + "dp": "Bs9oE8bmvGs9KRwjd2hjrjEe3MiC8mEQ8MX9W4anQE88wvx2x0Jy-gd4krfIdi4jOy45LuZ4-MKndMOXlHAbdK5GRsl73-KAe6688abV4zrNRJlMy1NPeFtx4TlkIqNiASYzOreyfs0Knb5PG3XxyYRhSMlq83mJJlI2t93xEwVOdTf721MzVTisiec55jYLNh0feLICc5ZYYOwZNrXL-AZu5Svg43oXc--iyY_T0niWPicKKDLG9mFsOHPD7HkfPiii6MSac7DCJd6qqq2tDTtv8yLTaP9uwKa6AclYFWx5cgiF4WI0wXbO7rHn3g_jiHd1hvyu46lxEfi3FkT6wQ", + "dq": "Nh7Eu8oB78N_nNZI5wbHKyQHeFQjmiL7MiJ1-i0dPmDyk80ns_ozZcH15Hg6d1hZD_us1vHO3o693l5e8p7jwPqGOf-364rLzR4dcIbb0UyWtrrGOkwYLfTSlzLZxpxi6WG0Tk9TygLclpariENv8YaK8bk8sycTJckHIYxFIVNrPR-Kj2kpsmwJ8iKtuT7z3u8Cwl_S2H2_pS84hVWDdR0e3OZwgi6QV1hZEmPw3A3qVU_rVqR9g4KVp6IT3u_xi--ugDVrovr3xg_YBXklh-bzPJLfcS0FxDWRoJke-ELKgCWFm0S78JrmRrN7m6KcHLrwAoWVAmfHKqMEVhZlnw", + "qi": "Qp55BbUd_Ef34ykWalS-NE9TzHgQ7XbSnSfaJ7Xs0uRcoP6EDF2-QhhTKkN55xu5LVTSbrzwmQNOGymL7ZQ6J_nXbrtviD2gzDLSyqAZ7lfBdXw8pCgf0r3EVhA16uq6EsmNQzj4Ec1RLe4taD9EXPgJvwQpvyFNZJ76RkVpzzadhGMD192645ElgdsiBegbJLI0iOMBI4dJHhvJ5on96WohUY5FBVnmibvA_sIAJfyyuoHYPxJYRkRVx18NoQR9eYrUsJOWNYM7GYL4zbi0iev14BuDhUg1b-PlTAnZTleuY-eTrkFrW99TkVU1M3DOQJ0pUcFjqE1x29xVGRJJuA" + } + ] + } diff --git a/automation/dbildungs-iam-server/cron/scripts/get_access_token.sh b/automation/dbildungs-iam-server/cron/scripts/get_access_token.sh new file mode 100644 index 000000000..b9b55d987 --- /dev/null +++ b/automation/dbildungs-iam-server/cron/scripts/get_access_token.sh @@ -0,0 +1,179 @@ +#!/bin/bash + +# Ensure the script exits on any error +set -e + +# Function to perform base64 URL encoding +base64url_encode() { + # Base64 encode the input, replace '+' with '-', '/' with '_', and remove padding '=' + echo -n "$1" | openssl enc -base64 -A | tr '+/' '-_' | tr -d '=' +} + +base64url_decode() { + local input=$1 + # Replace '-' with '+', '_' with '/' + input=$(echo "$input" | sed 's/-/+/g; s/_/\//g') + # Calculate the required padding + local padding=$(( (4 - ${#input} % 4) % 4 )) + # Add padding if necessary + input="$input$(printf '=%.0s' $(seq 1 $padding))" + echo "$input" | base64 -d +} + + +# Function to decode base64url and convert to hex, preserving leading zeros +decode_to_hex() { + base64url_decode "$1" | hexdump -v -e '/1 "%02x"' +} + +# Generate a random string for 'jti' claim +generate_jti() { + head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13 +} + +# Load environment variables +clientId="${KC_CLIENT_ID}" +kc_token_url="${KC_TOKEN_URL}" +backend_hostname="${BACKEND_HOSTNAME}" + +echo "BACKEND_HOSTNAME is $backend_hostname" +echo "KC_CLIENT_ID is $clientId" +echo "KC_TOKEN_URL is $kc_token_url" +echo "JWKS_FILE_PATH is $JWKS_FILE_PATH" + +# Load JWKS from environment variable or file +if [ -n "$JWKS" ]; then + # JWKS is set in the environment, use it directly + jwks="$JWKS" +elif [ -n "$JWKS_FILE_PATH" ] && [ -f "$JWKS_FILE_PATH" ]; then + # JWKS_FILE_PATH is set, use the file + jwks=$(cat "$JWKS_FILE_PATH") +else + echo "Error: No JWKS environment variable or JWKS file found." >> /var/log/cron.log + exit 1 +fi + +# Check if environment variables are set +if [[ -z "$clientId" || -z "$kc_token_url" || -z "$jwks" ]]; then + echo "Error: CLIENT_ID, TOKEN_URL, and JWKS environment variables must be set." >> /var/log/cron.log + exit 1 +fi + +# Extract the first key from the JWKS +key_json=$(echo "$jwks" | jq -c '.keys[0]') + +# Check if key_json is empty +if [[ -z "$key_json" ]]; then + echo "Error: No keys found in JWKS." >> /var/log/cron.log + exit 1 +fi + +# Extract RSA components from JWK +n=$(echo "$key_json" | jq -r '.n') +e=$(echo "$key_json" | jq -r '.e') +d=$(echo "$key_json" | jq -r '.d') +p=$(echo "$key_json" | jq -r '.p') +q=$(echo "$key_json" | jq -r '.q') +dp=$(echo "$key_json" | jq -r '.dp') +dq=$(echo "$key_json" | jq -r '.dq') +qi=$(echo "$key_json" | jq -r '.qi') + +# Decode the base64url-encoded components and convert to hex +n_dec=$(decode_to_hex "$n") +e_dec=$(decode_to_hex "$e") +d_dec=$(decode_to_hex "$d") +p_dec=$(decode_to_hex "$p") +q_dec=$(decode_to_hex "$q") +dp_dec=$(decode_to_hex "$dp") +dq_dec=$(decode_to_hex "$dq") +qi_dec=$(decode_to_hex "$qi") + +# Create an ASN.1 structure for the RSA private key +asn1_structure=$(mktemp) + +cat > "$asn1_structure" <> /var/log/cron.log + +# Generate the PEM-formatted private key +temp_key_file=$(mktemp) +openssl asn1parse -genconf "$asn1_structure" -out "$temp_key_file" > /dev/null 2>&1 +openssl rsa -in "$temp_key_file" -inform DER -outform PEM -out "$temp_key_file.pem" > /dev/null 2>&1 + +echo "Ending to generate PEM-formatted private key" >> /var/log/cron.log + +# Remove temporary files +rm "$asn1_structure" "$temp_key_file" + +# Create JWT header +header='{"alg":"RS256","typ":"JWT"}' +header_base64=$(base64url_encode "$header") + +# Create JWT payload +current_time=$(date +%s) +exp_time=$((current_time + 300)) # Token valid for 5 minutes +jti=$(generate_jti) + +payload=$(cat <> /var/log/cron.log + +# Sign the JWT +signature=$(echo -n "$header_payload" | \ + openssl dgst -sha256 -sign "$temp_key_file.pem" | \ + openssl enc -base64 -A | tr '+/' '-_' | tr -d '=') + +echo "Signed the JWT" >> /var/log/cron.log + +# Remove the temporary PEM key file +rm "$temp_key_file.pem" + +# Create the JWT assertion +jwt_assertion="$header_payload.$signature" + +# Make the POST request to Keycloak to get the access token +response=$(curl -s -X POST "$kc_token_url" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "grant_type=client_credentials" \ + -d "client_id=$clientId" \ + -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \ + -d "client_assertion=$jwt_assertion") + +echo "Access token requested" >> /var/log/cron.log + +# Check if the response contains an access token +if echo "$response" | grep -q '"access_token"'; then + # Extract the access token from the response + access_token=$(echo "$response" | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p') + echo "$access_token" +else + echo "Failed to retrieve access token. Response:" >> /var/log/cron.log + echo "$response" >> /var/log/cron.log + exit 1 +fi diff --git a/automation/dbildungs-iam-server/cron/scripts/trigger_xyz.sh b/automation/dbildungs-iam-server/cron/scripts/trigger_xyz.sh new file mode 100644 index 000000000..d82c59bf9 --- /dev/null +++ b/automation/dbildungs-iam-server/cron/scripts/trigger_xyz.sh @@ -0,0 +1,8 @@ +#!/bin/bash +echo "Trigger XYZ executed at $(date)" >> /var/log/cron.log + +# Call get_access_token.sh and capture the access token +access_token=$(./get_access_token.sh) + +# Use the access token as needed +echo "Access token obtained: $access_token" diff --git a/automation/dbildungs-iam-server/templates/configmap.yaml b/automation/dbildungs-iam-server/templates/configmap.yaml index 6b8d8ac73..d98ee1a8d 100644 --- a/automation/dbildungs-iam-server/templates/configmap.yaml +++ b/automation/dbildungs-iam-server/templates/configmap.yaml @@ -17,4 +17,4 @@ data: FRONTEND_LOGOUT_REDIRECT: "https://{{ .Values.backendHostname }}/" BACKEND_HOSTNAME: "{{ .Values.backendHostname }}" LDAP_URL: '{{ .Values.ldap.url | replace "spsh-xxx" .Release.Namespace }}' - LDAP_BIND_DN: "{{ .Values.ldap.bindDN }}" + LDAP_BIND_DN: "{{ .Values.ldap.bindDN }}" \ No newline at end of file diff --git a/automation/dbildungs-iam-server/templates/cronjob-envs-configmap.yaml b/automation/dbildungs-iam-server/templates/cronjob-envs-configmap.yaml new file mode 100644 index 000000000..8828d2d2b --- /dev/null +++ b/automation/dbildungs-iam-server/templates/cronjob-envs-configmap.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: cronjob-envs-configmap + namespace: {{ template "common.names.namespace" . }} + labels: + {{- include "common.labels" . | nindent 4 }} +data: + KC_TOKEN_URL: "https://{{ $.Values.keycloakHostname }}{{ $.Values.cronjobs.keycloakTokenUrl }}" + JWKS_FILE_PATH: "{{ $.Values.cronjobs.jwksFilePath }}" + KC_CLIENT_ID: "{{ $.Values.cronjobs.keycloakClientId }}" \ No newline at end of file diff --git a/automation/dbildungs-iam-server/templates/cronjob-scripts-configmap.yaml b/automation/dbildungs-iam-server/templates/cronjob-scripts-configmap.yaml new file mode 100644 index 000000000..e96498c20 --- /dev/null +++ b/automation/dbildungs-iam-server/templates/cronjob-scripts-configmap.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: cronjob-scripts-configmap + namespace: {{ template "common.names.namespace" . }} + labels: + {{- include "common.labels" . | nindent 4 }} +type: Opaque +data: + get_access_token.sh: |- + {{ .Files.Get "cron/scripts/get_access_token.sh" | nindent 4 }} + trigger_xyz.sh: |- + {{ .Files.Get "cron/scripts/trigger_xyz.sh" | nindent 4 }} \ No newline at end of file diff --git a/automation/dbildungs-iam-server/templates/cronjob-secrets-configmap.yaml b/automation/dbildungs-iam-server/templates/cronjob-secrets-configmap.yaml new file mode 100644 index 000000000..56fcc4954 --- /dev/null +++ b/automation/dbildungs-iam-server/templates/cronjob-secrets-configmap.yaml @@ -0,0 +1,10 @@ +apiVersion: onepassword.com/v1 +kind: OnePasswordItem +metadata: + name: cronjob-secret-service-acc-jwks + namespace: {{ template "common.names.namespace" . }} + labels: + app.kubernetes.io/managed-by: helm +spec: + itemPath: "vaults/spsh-dev-schulportal/items/dbildungs-iam-server" + \ No newline at end of file diff --git a/automation/dbildungs-iam-server/templates/cronjob.yaml b/automation/dbildungs-iam-server/templates/cronjob.yaml new file mode 100644 index 000000000..fafb423a4 --- /dev/null +++ b/automation/dbildungs-iam-server/templates/cronjob.yaml @@ -0,0 +1,70 @@ +{{- range $job_name, $job_options := .Values.cronjobs.jobs }} +apiVersion: batch/v1 +kind: CronJob +metadata: + name: dbp-1022-cronjob-dev-{{ $job_name}} + #name: {{ template "common.names.namespace" $ }}-{{ $job_name}} + namespace: {{ template "common.names.namespace" $ }} +spec: + schedule: {{ $job_options.schedule }} + startingDeadlineSeconds: 300 + suspend: true + jobTemplate: + spec: + backoffLimit: 0 + template: + metadata: + labels: + test: dbildungs-iam-server-{{ $job_name }} + pod: {{ $job_name }} + spec: + automountServiceAccountToken: false + containers: + - name: {{ $job_name }} + image: alpine:{{ $.Values.cronjobs.imageTag }} + imagePullPolicy: Always + securityContext: + allowPrivilegeEscalation: false + envFrom: + - configMapRef: + name: cronjob-envs-configmap + env: + - name: BACKEND_HOSTNAME + value: "{{ $.Values.backendHostname }}{{ $job_options.endpoint }}" + command: + - "sh" + - "-c" + - | + apk update && + apk add --no-cache bash openssl curl cronie jq vim && + mkdir /scripts && + cp /scripts_tmp/*.sh /scripts/ && + chmod +x /scripts/*.sh && + chmod 600 /etc/crontabs/root && + touch /var/log/cron.log && + chmod 644 /var/log/cron.log && + cd {{ $.Values.cronjobs.scriptDir}} && + ./{{ $job_options.script}} + volumeMounts: + - name: secret-volume-jwks + mountPath: /keys + readOnly: true + - name: script-volume + mountPath: /scripts_tmp + readOnly: false + ports: + - containerPort: {{ $.Values.cronjobs.port }} + name: trigger-xyz-pod + volumes: + - name: script-volume + configMap: + name: cronjob-scripts-configmap + - name: secret-volume-jwks + secret: + secretName: cronjob-secret-service-acc-jwks + items: + - key: service-account-private-jwks + path: jwks.json + restartPolicy: Never +--- +{{- end}} \ No newline at end of file diff --git a/automation/dbildungs-iam-server/values.yaml b/automation/dbildungs-iam-server/values.yaml index 42f40b11a..b1d43d9fe 100644 --- a/automation/dbildungs-iam-server/values.yaml +++ b/automation/dbildungs-iam-server/values.yaml @@ -124,12 +124,10 @@ backend: name: secret-volume extraVolumeMounts: [] - # Reference: https://github.com/bitnami/charts/tree/main/bitnami/redis-cluster redis-cluster: enabled: true - persistence: - enabled: false + persistence: size: 4Gi image: registry: docker.io @@ -137,14 +135,14 @@ redis-cluster: tag: 7.4 existingSecret: dbildungs-iam-server-redis cluster: - ## This is total number of nodes including the replicas. Meaning there will be 3 master and 3 replica - ## nodes (as replica count is set to 1 by default, there will be 1 replica per master node). - ## Hence, nodes = numberOfMasterNodes + numberOfMasterNodes * replicas - ## The number of master nodes should always be >= 3, otherwise cluster creation will fail - nodes: 6 - # for staging and prod this could get increased - ## @param cluster.replicas Number of replicas for every master in the cluster - replicas: 1 + ## This is total number of nodes including the replicas. Meaning there will be 3 master and 3 replica + ## nodes (as replica count is set to 1 by default, there will be 1 replica per master node). + ## Hence, nodes = numberOfMasterNodes + numberOfMasterNodes * replicas + ## The number of master nodes should always be >= 3, otherwise cluster creation will fail + nodes: 6 + # for staging and prod this could get increased + ## @param cluster.replicas Number of replicas for every master in the cluster + replicas: 1 networkPolicy: enabled: false pdb: @@ -155,9 +153,9 @@ redis-cluster: tls: enabled: false podLabels: - app.kubernetes.io/component: server-redis + app.kubernetes.io/component: server-redis commonLabels: - app.kubernetes.io/name: dbildungs-iam-server + app.kubernetes.io/name: dbildungs-iam-server resources: limits: cpu: 300m @@ -174,5 +172,27 @@ redis-cluster: requests: cpu: 50m memory: 64Mi - serviceMonitor: - enabled: true \ No newline at end of file + serviceMonitor: + enabled: true + +cronjobs: + imageTag: latest + port: 5656 + keycloakTokenUrl: '/realms/SPSH/protocol/openid-connect/token' + keycloakClientId: spsh-service + jwksFilePath: /keys/jwks.json + backendHostname: '{{ $.Values.frontendHostname }}' + scriptDir: scripts + jobs: + trigger-xyz-1: + schedule: 0 8 * * * + endpoint: '' + script: 'trigger_xyz.sh' + trigger-xyz-2: + schedule: 0 8 * * * + endpoint: '' + script: 'trigger_xyz.sh' + trigger-xyz-3: + schedule: 0 8 * * * + endpoint: '' + script: 'trigger_xyz.sh'