Skip to content

Commit

Permalink
fix: improve k8s keycloak bootstrapping script (#1278)
Browse files Browse the repository at this point in the history
Signed-off-by: Pat Losoponkul <[email protected]>
  • Loading branch information
patlo-iog authored Aug 7, 2024
1 parent 758fe87 commit cfc4ccf
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ object EndpointOutputs {

val basicFailures: EndpointOutput[ErrorResponse] = basicFailuresWith()

val basicFailuresAndForbidden = basicFailuresWith(FailureVariant.forbidden)
val basicFailuresAndForbidden = basicFailuresWith(FailureVariant.unauthorized, FailureVariant.forbidden)

val basicFailuresAndNotFound = basicFailuresWith(FailureVariant.notFound)

val basicFailureAndNotFoundAndForbidden = basicFailuresWith(FailureVariant.notFound, FailureVariant.forbidden)
val basicFailureAndNotFoundAndForbidden =
basicFailuresWith(FailureVariant.notFound, FailureVariant.unauthorized, FailureVariant.forbidden)

object FailureVariant {
val badRequest = oneOfVariantValueMatcher(
Expand Down Expand Up @@ -57,6 +58,11 @@ object EndpointOutputs {
StatusCode.Forbidden,
jsonBody[ErrorResponse].description("Forbidden")
)(statusCodeMatcher(StatusCode.Forbidden))

val unauthorized = oneOfVariantValueMatcher(
StatusCode.Unauthorized,
jsonBody[ErrorResponse].description("Unauthorized")
)(statusCodeMatcher(StatusCode.Unauthorized))
}

}
1 change: 1 addition & 0 deletions infrastructure/charts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/charts/*.tgz
183 changes: 101 additions & 82 deletions infrastructure/charts/agent/templates/configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,92 +5,111 @@ metadata:
labels:
{{- include "labels.common" . | nindent 4 }}
data:
init.sh: |
#!/usr/bin/env bash
init.ts: |
const KEYCLOAK_BASE_URL = process.env.KEYCLOAK_BASE_URL!;
const KEYCLOAK_ADMIN_USER = process.env.KEYCLOAK_ADMIN_USER!;
const KEYCLOAK_ADMIN_PASSWORD = process.env.KEYCLOAK_ADMIN_PASSWORD!;
const REALM_NAME = process.env.REALM_NAME!;
const CLOUD_AGENT_CLIENT_ID = process.env.CLOUD_AGENT_CLIENT_ID!;
const CLOUD_AGENT_CLIENT_SECRET = process.env.CLOUD_AGENT_CLIENT_SECRET!;
set -e
set -u
KEYCLOAK_BASE_URL=$KEYCLOAK_BASE_URL
KEYCLOAK_ADMIN_USER=$KEYCLOAK_ADMIN_USER
KEYCLOAK_ADMIN_PASSWORD=$KEYCLOAK_ADMIN_PASSWORD
REALM_NAME=$REALM_NAME
CLOUD_AGENT_CLIENT_ID=$CLOUD_AGENT_CLIENT_ID
CLOUD_AGENT_CLIENT_SECRET=$CLOUD_AGENT_CLIENT_SECRET
function get_admin_token() {
local response=$(
curl --request POST "$KEYCLOAK_BASE_URL/realms/master/protocol/openid-connect/token" \
--fail -s -v \
--data-urlencode "grant_type=password" \
--data-urlencode "client_id=admin-cli" \
--data-urlencode "username=$KEYCLOAK_ADMIN_USER" \
--data-urlencode "password=$KEYCLOAK_ADMIN_PASSWORD"
)
local access_token=$(echo $response | jq -r '.access_token')
echo $access_token
}
function is_client_exists() {
local access_token=$1
local client_id=$2
local http_status=$(
curl --request GET "$KEYCLOAK_BASE_URL/admin/realms/$REALM_NAME/clients/$client_id" \
-s -w "%{http_code}" \
-o /dev/null \
-H "Authorization: Bearer $access_token"
)
if [ $http_status == 200 ]; then
echo "true"
else
echo "false"
fi
async function getAdminToken(): Promise<string> {
const req = new Request(`${KEYCLOAK_BASE_URL}/realms/master/protocol/openid-connect/token`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
"grant_type": "password",
"client_id": "admin-cli",
"username": KEYCLOAK_ADMIN_USER,
"password": KEYCLOAK_ADMIN_PASSWORD,
})
});
const resp = await fetch(req);
const body = await resp.json();
if (resp.status !== 200) {
throw new Error("Response did not return success status code." +
` Status: ${resp.status}, Body: ${JSON.stringify(body)}`
);
}
return body["access_token"];
}
function create_client() {
local access_token=$1
local client_id=$2
local client_secret=$3
curl --request POST "$KEYCLOAK_BASE_URL/admin/realms/$REALM_NAME/clients" \
--fail -s \
-H "Authorization: Bearer $access_token" \
-H "Content-Type: application/json" \
--data-raw "{
\"id\": \"$client_id\",
\"directAccessGrantsEnabled\": true,
\"authorizationServicesEnabled\": true,
\"serviceAccountsEnabled\": true,
\"secret\": \"$client_secret\"
}"
async function createRealm(accessToken: string) {
console.log(`Creating realm ${REALM_NAME} ...`);
const req = new Request(`${KEYCLOAK_BASE_URL}/admin/realms`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${accessToken}`
},
body: JSON.stringify({
"realm": REALM_NAME,
"enabled": true,
})
});
const resp = await fetch(req);
const resp_body = await resp.json();
switch (resp.status) {
case 201:
console.log(`Realm ${REALM_NAME} created.`);
break;
case 409:
console.log(`Realm ${REALM_NAME} already exists.`);
break;
default:
throw new Error("Response did not return success status code." +
` Status: ${resp.status}, Body: ${JSON.stringify(resp_body)}`
);
}
}
echo "Getting admin access token ..."
ADMIN_ACCESS_TOKEN=$(get_admin_token)
CLIENT_EXIST=$(is_client_exists $ADMIN_ACCESS_TOKEN $CLOUD_AGENT_CLIENT_ID)
if [ $CLIENT_EXIST == "false" ]; then
echo "Creating a new $CLOUD_AGENT_CLIENT_ID client ..."
create_client $ADMIN_ACCESS_TOKEN $CLOUD_AGENT_CLIENT_ID $CLOUD_AGENT_CLIENT_SECRET
fi
async function createClient(accessToken: string, clientId: string, clientSecret?: string) {
console.log(`Creating client ${clientId} ...`);
let req_body = {};
if (clientSecret) {
req_body = {
"id": clientId,
"directAccessGrantsEnabled": true,
"authorizationServicesEnabled": true,
"serviceAccountsEnabled": true,
"secret": clientSecret
};
} else {
// public client is created if client secret is not provided
req_body = {
"id": clientId,
"publicClient": true,
"consentRequired": true,
"redirectUris": ["*"]
};
}
{{- if .Values.keycloak.enabled }}

---

apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "cloud-agent.name" . }}-realm-import
labels:
{{- include "labels.common" . | nindent 4}}
data:
{{ include "cloud-agent.name" . }}.json: |
{
"realm": {{ .Values.server.keycloak.realm | quote }},
"enabled": true
const req = new Request(`${KEYCLOAK_BASE_URL}/admin/realms/${REALM_NAME}/clients`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${accessToken}`
},
body: JSON.stringify(req_body)
});
const resp = await fetch(req);
const resp_body = await resp.json();
switch (resp.status) {
case 201:
console.log(`Client ${clientId} created.`);
break;
case 409:
console.log(`Client ${clientId} already exists.`);
break;
default:
throw new Error("Response did not return success status code." +
` Status: ${resp.status}, Body: ${JSON.stringify(resp_body)}`
);
}
}
{{- end }}
(async () => {
console.log("Getting admin access token ...");
const adminToken = await getAdminToken();
await createRealm(adminToken);
await createClient(adminToken, CLOUD_AGENT_CLIENT_ID, CLOUD_AGENT_CLIENT_SECRET);
})();
15 changes: 6 additions & 9 deletions infrastructure/charts/agent/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,13 @@ spec:
image: busybox
command: ['sh', '-c', "until nc -z {{ .Values.database.postgres.managingTeam }}-{{ include "cloud-agent.name" . }}-postgres-cluster.{{ .Release.Namespace }} 5432; do echo waiting for postgress-operator; sleep 2; done;"]
{{- if .Values.server.keycloak.enabled }}
- name: wait-keycloak-ready
image: badouralix/curl-jq:ubuntu
command: ['/bin/bash', '-c', 'until curl http://{{ .Release.Name }}-keycloak/health/ready; do sleep 2; done && echo "Keycloak is ready."']
{{- if .Values.server.keycloak.bootstrap }}
- name: keycloak-bootstrap
image: badouralix/curl-jq:ubuntu
command: ['/bin/bash', '-c', '/scripts/init.sh']
image: oven/bun:1
command: ['bun', 'run', '/scripts/init.ts']
env:
- name: KEYCLOAK_BASE_URL
value: http://{{ .Release.Name }}-keycloak
value: {{ .Values.server.keycloak.url }}
- name: KEYCLOAK_ADMIN_USER
value: {{ .Values.server.keycloak.admin.username }}
- name: KEYCLOAK_ADMIN_PASSWORD
Expand Down Expand Up @@ -202,7 +199,7 @@ spec:
- name: KEYCLOAK_ENABLED
value: "true"
- name: KEYCLOAK_URL
value: http://{{ .Release.Name }}-keycloak
value: {{ .Values.server.keycloak.url }}
- name: KEYCLOAK_REALM
value: {{ .Values.server.keycloak.realm }}
- name: KEYCLOAK_CLIENT_ID
Expand All @@ -222,8 +219,8 @@ spec:
name: keycloak-bootstrap-script
defaultMode: 0500
items:
- key: "init.sh"
path: "init.sh"
- key: "init.ts"
path: "init.ts"
{{- end }}
affinity:
{{- toYaml .Values.affinity | nindent 8 }}
Expand Down
16 changes: 3 additions & 13 deletions infrastructure/charts/agent/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ server:
useVault: true
keycloak:
enabled: false
url: http://agent-keycloak
realm: prism-agent
bootstrap: true
admin:
Expand All @@ -59,7 +60,7 @@ server:
name: keycloak-admin-secret
key: password
client:
clientId: prism-agent
clientId: cloud-agent
clientSecret:
secretKeyRef:
name: agent-keycloak-client-secret
Expand Down Expand Up @@ -136,7 +137,7 @@ vault:
keycloak:
enabled: false
# --hostname-url should be the frontend url that user will be logging in with keycloak
extraStartupArgs: "--hostname-url=http://localhost:8080 --import-realm --features=declarative-user-profile"
extraStartupArgs: "--hostname-url=http://localhost:8080 --features=declarative-user-profile"
tls:
enabled: true
autoGenerated: true
Expand All @@ -154,17 +155,6 @@ keycloak:
port: "5432"
user: keycloak-admin
database: keycloak
extraVolumes:
- name: cloud-agent-realm-import-volume
configMap:
name: cloud-agent-realm-import
items:
- key: cloud-agent.json
path: cloud-agent.json
extraVolumeMounts:
- name: cloud-agent-realm-import-volume
mountPath: /opt/bitnami/keycloak/data/import
readOnly: true

# It is configured for deployment and postgresql objects of cloud-agent
affinity:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ import org.hyperledger.identus.pollux.core.service.serdes.{AnoncredCredentialPro
import org.hyperledger.identus.pollux.sdjwt.{HolderPrivateKey, PresentationCompact}
import org.hyperledger.identus.pollux.vc.jwt.{Issuer, PresentationPayload, W3cCredentialPayload}
import org.hyperledger.identus.shared.models.*
import zio.{mock, IO, URLayer, ZIO, ZLayer}
import zio.{mock, IO, UIO, URLayer, ZIO, ZLayer}
import zio.json.*
import zio.mock.{Mock, Proxy}
import zio.UIO

import java.time.Instant
import java.util.UUID
Expand Down

0 comments on commit cfc4ccf

Please sign in to comment.