Skip to content

Commit

Permalink
dbildungs-iam-server
Browse files Browse the repository at this point in the history
  • Loading branch information
dbildungs-iam-server-gha committed Nov 20, 2024
1 parent 32972af commit 88924a9
Show file tree
Hide file tree
Showing 14 changed files with 435 additions and 49 deletions.
2 changes: 1 addition & 1 deletion automation/dbildungs-iam-server/Chart.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ dependencies:
repository: https://charts.bitnami.com/bitnami
version: 11.0.6
digest: sha256:790bafa04fe9c1cc9f772dc12fada16eb847c282f738fd23df09f665af93ec74
generated: "2024-11-20T15:03:02.334662439Z"
generated: "2024-11-20T15:09:27.026824035Z"
4 changes: 2 additions & 2 deletions automation/dbildungs-iam-server/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
apiVersion: v2
appVersion: DBP-1066
appVersion: DBP-1022
dependencies:
- condition: redis-cluster.enabled
name: redis-cluster
Expand All @@ -8,4 +8,4 @@ dependencies:
description: dBildungs-IAM-server
name: dbildungs-iam-server
type: application
version: 0.0.0-dbp-1066-20241120-1502
version: 0.0.0-dbp-1022-20241120-1509
23 changes: 23 additions & 0 deletions automation/dbildungs-iam-server/cron/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM alpine:3.19

# Install necessary packages
RUN apk update && \
apk add --no-cache bash cronie jq openssl vim wget

# Create a new user and group
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# Copy scripts into the image
COPY scripts/ /scripts/

# Set execute permissions for all .sh scripts in /scripts/ and create a log file
RUN chmod +x /scripts/*.sh \
&& touch /var/log/cron.log \
&& chmod 644 /var/log/cron.log \
&& chown -R appuser:appgroup /scripts /var/log/cron.log

# Switch to the new user
USER appuser

# Start the cron service in foreground
CMD ["/usr/sbin/crond", "-f"]
15 changes: 15 additions & 0 deletions automation/dbildungs-iam-server/cron/keys/dummy_jwks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"keys": [
{
"kty": "RSA",
"n": "23mzd3v4YjgWMzO7XYYwD92NqCm436ErU1-NPTVok9aaVXx5mjZKfh_Xoyp5BEgjQU042MKOhl1Ri17HfOOf6k4cpBpvBQhENp0yfNPv_-kSy4OgdA3qk9-kZyvuRX1-0LKvJMwlrCLCLEfiv_yn8YQLpQeqgdIj1AlX37fcnSxxL3qukM_-Hm8dCB2mbUzANT_uRkSCHFQWVDxcbocKAmhr0808CmpiINWEIVv7AhS_HVSliaeB-iteAKN3W9Am3tCtGZaWKUlioKueQux7OTKxHm5fM-jZ9ZPnb7_RQlOGV9vu-TTMO8pKYkqn15LcnYuBKKHmFEBO8vRxI9_8Lw",
"e": "AQAB",
"d": "CdcSByhlbC9BkjgejW89FmkDjJJE-gR63HkV7F70T7SOejNjga4vdtTXUTclR94yyR8SORMNWtQyRMJnb_UGBXZNGG_K9yR2EntyeQrzjBDCHqJ0fjTlheMVYZQkUbSdC_RcSpUQl1V-STKhOvmz-e3Gq-Evxt70wPFOTEyCYAA5zTSgF7vwoxtKChfOb3NvkLUmD4JrBEb0vzapTgVvoyB158glUGEibpHBaVvVnA98qEI5hqJE2jhhtaoGyvErIkWDOummb1WPN2D0Nqsvr-sfwH3mxKFLDogHIfjMLxDaP9Y3I7Wwie9pbpsg6zK66s6EB27hkZnbRLlwaK4ImQ",
"p": "_KCzohdV8BpnvfDxyL-Zjj8paJB5RBLkewf7xl-sqLHykjn-_nR1OGfEr8Gc0zwYD6FtTAJ9JN-h730vBacUVZDrgnKOW0NbQPIwNXCSisyChhbkSVXLBi94r_-t92ieJ8wPbchynF6Z1UyH0m4rieKnAPcxuio9iLuXdQrRNEs",
"q": "3me1bHQ_GO5mPKwUf-kSZDguninq98ERMOAYdr__yUM1fc8QJ_3FSkZsSFr91Fi5kPvP9gthPRYhlfKeix61ibypLnLpyx6A298VIdG8VFjPrXzlme5CGSPYN9-YRSQq31e-xSdkn3lKiJlqPZzlRARyHveJlSWu07LuS91AgS0",
"dp": "--U1GEOSchWyKaeNPrElaLu8C0I7WFBKOA7u0o9ldtPwXjOr-Yaftz1o1iMEv29lQnigpbC5ncHLEyRMdaNyWBtnaSvWnFNeMzUKMs7rn7Bp2VAMEr-T77f36-3SRiavxFjpbXr4JMkDNLbZm0405Yj1IrZYhBtIPgVm8NJ3ZV8",
"dq": "ofqgbKvBZLQEq_2cNIiYh3tPoIvhAK6Riao8xwgREBEt_UH4f1fY_76IkK4MnkI8bHapwIYLPQVIUsBQbfxgtT89bIHu-qttqDUyW944Lqo8HxuO0WxwoYS0rgTgDsNHokByxX5qT6dz_EbX1KXXaJFgWGNqxcCbMr3nxkMO_sU",
"qi": "r8ZslmjXzZJUv6IoN6nUT12UpzmhbriRXxjTcLNSwZBuSXz8QV_7F8ViNyEcot20aDo35t8IssLnDD9nxDAGTCL68FkXTJaAsUE2beGfkX9Sz5r_Gzlcer_Gjhl5aNHeZYgIMsYciPhM4laBzKD3d51xQuDFMMX1RQUvyDHDIog"
}
]
}
38 changes: 38 additions & 0 deletions automation/dbildungs-iam-server/cron/scripts/cron_trigger.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/bash

# Check if BACKEND_ENDPOINT_URL is set
if [ -z "$BACKEND_ENDPOINT_URL" ]; then
echo "Error: BACKEND_ENDPOINT_URL is not set."
exit 1
fi

# Check if HTTP_METHOD is set
if [ -z "$HTTP_METHOD" ]; then
echo "Error: HTTP_METHOD is not set."
exit 1
fi

endpoint_url="${BACKEND_ENDPOINT_URL}"

echo "Triggering $endpoint_url with $HTTP_METHOD at $(date)"

# Call get_access_token.sh and capture the access token
access_token=$(./get_access_token.sh)

# Make the request with JWT authorization and capture the HTTP status code and response body
response=$(wget --quiet --output-document=- --server-response --header="Authorization: Bearer $access_token" --header="Content-Type: application/json" --method="$HTTP_METHOD" "$endpoint_url" 2>&1)

# Split the response into body and status code
response_body=$(echo "$response" | sed -n '/^ HTTP\//q;p')
http_status=$(echo "$response" | awk '/^ HTTP\// {print $2; exit}')

# Output the response details
echo "Finished triggering $endpoint_url with $HTTP_METHOD at $(date)
HTTP Status: $http_status
Response Body: $response_body"

# Exit with status 1 if the HTTP status code is not 200
if [ "$http_status" -ne 200 ]; then
echo "Error: HTTP request failed with status code $http_status"
exit 1
fi
169 changes: 169 additions & 0 deletions automation/dbildungs-iam-server/cron/scripts/get_access_token.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#!/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}"

# 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" <<EOF
asn1=SEQUENCE:private_key
[private_key]
version=INTEGER:0
n=INTEGER:0x$n_dec
e=INTEGER:0x$e_dec
d=INTEGER:0x$d_dec
p=INTEGER:0x$p_dec
q=INTEGER:0x$q_dec
dp=INTEGER:0x$dp_dec
dq=INTEGER:0x$dq_dec
qi=INTEGER:0x$qi_dec
EOF

echo "Starting to generate PEM-formatted private key" >> /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 <<EOF
{
"iss": "$clientId",
"sub": "$clientId",
"aud": "$kc_token_url",
"jti": "$jti",
"exp": $exp_time
}
EOF
)
payload_base64=$(base64url_encode "$payload")

# Combine header and payload
header_payload="$header_base64.$payload_base64"

echo "Payload created" >> /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=$(wget -qO- --post-data "grant_type=client_credentials&client_id=$clientId&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=$jwt_assertion" \
--header "Content-Type: application/x-www-form-urlencoded" \
"$kc_token_url")

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
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@
secretKeyRef:
name: {{ default .Values.auth.existingSecret .Values.auth.secretName }}
key: frontend-sessionSecret
- name: ITSLEARNING_ENABLED
valueFrom:
secretKeyRef:
name: {{ default .Values.auth.existingSecret .Values.auth.secretName }}
key: itslearning-enabled
- name: ITSLEARNING_ENDPOINT
valueFrom:
secretKeyRef:
name: {{ default .Values.auth.existingSecret .Values.auth.secretName }}
key: itslearning-endpoint
- name: ITSLEARNING_USERNAME
valueFrom:
secretKeyRef:
Expand All @@ -46,16 +56,16 @@
secretKeyRef:
name: {{ default .Values.auth.existingSecret .Values.auth.secretName }}
key: itslearning-password
- name: LDAP_BIND_DN
- name: LDAP_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: {{ default .Values.auth.existingSecret .Values.auth.secretName }}
key: ldap-bind-dn
- name: LDAP_ADMIN_PASSWORD
key: ldap-admin-password
- name: PI_BASE_URL
valueFrom:
secretKeyRef:
name: {{ default .Values.auth.existingSecret .Values.auth.secretName }}
key: ldap-admin-password
key: pi-base-url
- name: PI_ADMIN_USER
valueFrom:
secretKeyRef:
Expand All @@ -66,6 +76,21 @@
secretKeyRef:
name: {{ default .Values.auth.existingSecret .Values.auth.secretName }}
key: pi-admin-password
- name: PI_USER_RESOLVER
valueFrom:
secretKeyRef:
name: {{ default .Values.auth.existingSecret .Values.auth.secretName }}
key: pi-user-resolver
- name: PI_REALM
valueFrom:
secretKeyRef:
name: {{ default .Values.auth.existingSecret .Values.auth.secretName }}
key: pi-user-realm
- name: PI_RENAME_WAITING_TIME
valueFrom:
secretKeyRef:
name: {{ default .Values.auth.existingSecret .Values.auth.secretName }}
key: pi-rename-waiting-time
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@ spec:
spec:
automountServiceAccountToken: false
initContainers:
{{- if .Values.backend.dbmigration.enabled }}
- name: "{{ template "common.names.name" . }}-db-migration-apply"
image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.backend.image.pullPolicy | default "Always" }}
securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }}
command: {{ .Values.backend.dbmigration.command }}
command: [ "node", "dist/src/console/main.js", "db", "migration-apply" ]
env:
{{- include "dbildungs-iam-server-backend-envs" . | indent 12 }}
{{- if .Values.backend.extraEnvVars }}
Expand All @@ -39,13 +38,11 @@ spec:
volumeMounts:
{{- toYaml .Values.backend.volumeMounts | nindent 12 }}
resources: {{- toYaml .Values.backend.resources | nindent 12 }}
{{ end }}
{{- if .Values.backend.keycloakdatamigration.enabled }}
- name: "{{ template "common.names.name" . }}-keycloak-data-migration"
image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.backend.image.pullPolicy | default "Always" }}
securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }}
command: {{ .Values.backend.keycloakdatamigration.command }}
command: [ "node", "dist/src/console/main.js", "keycloak", "update-clients", "dev" ]
env:
{{- include "dbildungs-iam-server-backend-envs" . | indent 12 }}
{{- if .Values.backend.extraEnvVars }}
Expand All @@ -57,21 +54,20 @@ spec:
volumeMounts:
{{- toYaml .Values.backend.volumeMounts | nindent 12 }}
resources: {{- toYaml .Values.backend.resources | nindent 12 }}
{{ end }}
{{- if .Values.backend.dbseeding.enabled }}
- name: db-seeding
image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{.Values.imagePullPolicy | default "Always"}}
securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }}
command: {{ .Values.backend.dbseeding.command }}
command: [ "node", "dist/src/console/main.js", "db", "seed", "dev" ]
envFrom:
- configMapRef:
name: {{ template "common.names.name" . }}
env: {{- include "dbildungs-iam-server-backend-envs" . | indent 12}}
volumeMounts:
{{- toYaml .Values.backend.volumeMounts | nindent 12 }}
resources: {{- toYaml .Values.backend.resources | nindent 12 }}
{{ end }}
{{end}}
containers:
- name: "{{ template "common.names.name" . }}-backend"
image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag | default .Chart.AppVersion }}"
Expand Down
Loading

0 comments on commit 88924a9

Please sign in to comment.