Skip to content

Commit

Permalink
Merge pull request #3841 from uselagoon/keycloak-admin-api-client
Browse files Browse the repository at this point in the history
refactor: use client to auth to keycloak instead of admin user
  • Loading branch information
tobybellwood authored Dec 17, 2024
2 parents 17fc987 + ce40646 commit 5e6dddc
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 16 deletions.
10 changes: 9 additions & 1 deletion local-dev/k3d-seed-data/configure-webauthn.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
#!/bin/bash

CONFIG_PATH=/tmp/kcadm.config
/opt/keycloak/bin/kcadm.sh config credentials --config $CONFIG_PATH --server http://localhost:8080/auth --user $KEYCLOAK_ADMIN_USER --password $KEYCLOAK_ADMIN_PASSWORD --realm master
if ! /opt/keycloak/bin/kcadm.sh config credentials --config $CONFIG_PATH --server http://localhost:8080/auth --realm master --client admin-api --secret ${KEYCLOAK_ADMIN_API_CLIENT_SECRET}
then
if ! /opt/keycloak/bin/kcadm.sh config credentials --config $CONFIG_PATH --server http://localhost:8080/auth --user $KEYCLOAK_ADMIN_USER --password $KEYCLOAK_ADMIN_PASSWORD --realm master
then
echo "Unable to log in to keycloak with client admin-api or username and password"
echo "If you have rotated the admin-api secret, you will need to log in and update it manually"
exit 1
fi
fi

if [ "$(/opt/keycloak/bin/kcadm.sh get authentication/flows -r "lagoon" --fields id,alias --config $CONFIG_PATH | jq -r '.[] | select(.alias==("Browser-Webauthn")) | .id')" == "" ]; then
echo "Creating browser flow for webauthn"
Expand Down
13 changes: 9 additions & 4 deletions local-dev/k3d-seed-data/seed-example-sso.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
CONFIG_PATH=/tmp/kcadm.config

# login to keycloak
/opt/keycloak/bin/kcadm.sh config credentials \
--config $CONFIG_PATH --server http://localhost:8080/auth \
--user $KEYCLOAK_ADMIN_USER --password $KEYCLOAK_ADMIN_PASSWORD \
--realm master
if ! /opt/keycloak/bin/kcadm.sh config credentials --config $CONFIG_PATH --server http://localhost:8080/auth --realm master --client admin-api --secret ${KEYCLOAK_ADMIN_API_CLIENT_SECRET}
then
if ! /opt/keycloak/bin/kcadm.sh config credentials --config $CONFIG_PATH --server http://localhost:8080/auth --user $KEYCLOAK_ADMIN_USER --password $KEYCLOAK_ADMIN_PASSWORD --realm master
then
echo "Unable to log in to keycloak with client admin-api or username and password"
echo "If you have rotated the admin-api secret, you will need to log in and update it manually"
exit 1
fi
fi

if /opt/keycloak/bin/kcadm.sh get realms/sso --config $CONFIG_PATH > /dev/null; then
echo "Realm sso is already created, skipping"
Expand Down
11 changes: 9 additions & 2 deletions local-dev/k3d-seed-data/seed-users.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,15 @@ function configure_keycloak {
CONFIG_PATH=/tmp/kcadm.config

echo Keycloak is running, proceeding with configuration

/opt/keycloak/bin/kcadm.sh config credentials --config $CONFIG_PATH --server http://localhost:8080/auth --user $KEYCLOAK_ADMIN_USER --password $KEYCLOAK_ADMIN_PASSWORD --realm master
if ! /opt/keycloak/bin/kcadm.sh config credentials --config $CONFIG_PATH --server http://localhost:8080/auth --realm master --client admin-api --secret ${KEYCLOAK_ADMIN_API_CLIENT_SECRET}
then
if ! /opt/keycloak/bin/kcadm.sh config credentials --config $CONFIG_PATH --server http://localhost:8080/auth --user $KEYCLOAK_ADMIN_USER --password $KEYCLOAK_ADMIN_PASSWORD --realm master
then
echo "Unable to log in to keycloak with client admin-api or username and password"
echo "If you have rotated the admin-api secret, you will need to log in and update it manually"
exit 1
fi
fi

configure_user_passwords

Expand Down
3 changes: 1 addition & 2 deletions services/api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,9 @@ RUN yarn check --verify-tree
# Making sure we run in production
ENV NODE_ENV=production \
LOGSDB_ADMIN_PASSWORD=admin \
KEYCLOAK_ADMIN_USER=admin \
KEYCLOAK_ADMIN_PASSWORD=admin \
ELASTICSEARCH_URL=http://logs-db-service:9200 \
KEYCLOAK_API_CLIENT_SECRET=39d5282d-3684-4026-b4ed-04bbc034b61a \
KEYCLOAK_ADMIN_API_CLIENT_SECRET=bb86d344-a52d-11ef-b872-4f4337ee24f0 \
REDIS_PASSWORD=admin

# The API is not very resilient to sudden mariadb restarts which can happen when the api and mariadb are starting
Expand Down
11 changes: 7 additions & 4 deletions services/api/src/clients/keycloak-admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { decode } from 'jsonwebtoken';
import { KeycloakAdminClient } from '@s3pweb/keycloak-admin-client-cjs';
import { logger } from '../loggers/logger';
import { config } from './keycloakClient';
import { getConfigFromEnv } from '../util/config';

/// Helper to type check try/catch. Remove when we can stop using the @s3pweb
// commonJS version.
Expand Down Expand Up @@ -49,10 +50,12 @@ export const getKeycloakAdminClient = async (): Promise<KeycloakAdminClient> =>
});

await keycloakAdminClient.auth({
username: config.user,
password: config.pass,
grantType: 'password',
clientId: 'admin-cli',
grantType: 'client_credentials',
clientId: 'admin-api',
clientSecret: getConfigFromEnv(
'KEYCLOAK_ADMIN_API_CLIENT_SECRET',
'<secret not set>'
),
});

keycloakAdminClient.setConfig({
Expand Down
2 changes: 0 additions & 2 deletions services/api/src/clients/keycloakClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { getConfigFromEnv, getLagoonRouteFromEnv } from '../util/config';

export const config = {
origin: getConfigFromEnv('KEYCLOAK_URL', 'http://keycloak:8080'),
user: getConfigFromEnv('KEYCLOAK_ADMIN_USER', 'admin'),
pass: getConfigFromEnv('KEYCLOAK_ADMIN_PASSWORD', '<password not set>'),
realm: 'lagoon',
apiClientSecret: getConfigFromEnv(
'KEYCLOAK_API_CLIENT_SECRET',
Expand Down
1 change: 1 addition & 0 deletions services/keycloak/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ ENV TMPDIR=/tmp \
KEYCLOAK_AUTH_SERVER_CLIENT_SECRET=f605b150-7636-4447-abd3-70988786b330 \
KEYCLOAK_SERVICE_API_CLIENT_SECRET=d3724d52-34d1-4967-a802-4d178678564b \
KEYCLOAK_LAGOON_UI_OIDC_CLIENT_SECRET=20580a56-6fbc-11ef-9a5b-3b4da292aa54 \
KEYCLOAK_ADMIN_API_CLIENT_SECRET=bb86d344-a52d-11ef-b872-4f4337ee24f0 \
LAGOON_DB_VENDOR=mariadb \
LAGOON_DB_DATABASE=infrastructure \
LAGOON_DB_USER=api \
Expand Down
34 changes: 33 additions & 1 deletion services/keycloak/startup-scripts/00-configure-lagoon.sh
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,23 @@ function configure_lagoon_redirect_uris {
fi
}

function configure_admin_api_client {
# this client is used by the lagoon api to perform actions against keycloak without needing to use the username and password
# this allows for configuring the username and password of the admin account with 2fa if required
admin_api_client_id=$(/opt/keycloak/bin/kcadm.sh get -r master clients?clientId=admin-api --config $CONFIG_PATH)
if [ "$admin_api_client_id" != "[ ]" ]; then
echo "Client admin-api is already created, skipping basic setup"
return 0
fi
echo Creating client admin-api
echo '{"clientId": "admin-api", "publicClient": false, "standardFlowEnabled": false, "serviceAccountsEnabled": true, "secret": "'${KEYCLOAK_ADMIN_API_CLIENT_SECRET}'"}' | /opt/keycloak/bin/kcadm.sh create clients --config $CONFIG_PATH -r master -f -
ADMIN_API_CLIENT_ID=$(/opt/keycloak/bin/kcadm.sh get -r master clients?clientId=admin-api --config $CONFIG_PATH | jq -r '.[0]["id"]')
echo Enable fine grained permissions
/opt/keycloak/bin/kcadm.sh update clients/$ADMIN_API_CLIENT_ID/management/permissions --config $CONFIG_PATH -r master -s enabled=true

/opt/keycloak/bin/kcadm.sh add-roles -r master --uusername service-account-admin-api --rolename admin --config $CONFIG_PATH
}

##############
# Migrations #
##############
Expand Down Expand Up @@ -866,7 +883,21 @@ function configure_keycloak {

echo Keycloak is running, proceeding with configuration

/opt/keycloak/bin/kcadm.sh config credentials --config $CONFIG_PATH --server http://localhost:8080/auth --user $KEYCLOAK_USER --password $KEYCLOAK_PASSWORD --realm master
# attempt to log in with the admin-api client service account
# this can fail the first time as the admin-api client might not exist because it is called by 'configure_admin_api_client'
# it will then fall back to using the username and password to authenticate against keycloak
# this has the same downside as the username/password problem in that if the user password or the admin-api client secret are ever rotated
# then they will need to be changed in keycloak at the same time that the changes are applied when rotating them via lagoon if they are being changed
# otherwise there is currently no way to change these without knowing the previous password or client secret
if ! /opt/keycloak/bin/kcadm.sh config credentials --config $CONFIG_PATH --server http://localhost:8080/auth --realm master --client admin-api --secret ${KEYCLOAK_ADMIN_API_CLIENT_SECRET}
then
if ! /opt/keycloak/bin/kcadm.sh config credentials --config $CONFIG_PATH --server http://localhost:8080/auth --user $KEYCLOAK_USER --password $KEYCLOAK_PASSWORD --realm master
then
echo "Unable to log in to keycloak with client admin-api or keycloak admin username and password"
echo "If you have rotated the admin-api secret, you will need to log in and update it manually"
exit 1
fi
fi

# Sets the order of migrations, add new ones at the end.
import_lagoon_realm
Expand All @@ -875,6 +906,7 @@ function configure_keycloak {
configure_smtp_settings
configure_realm_settings
configure_lagoon_redirect_uris
configure_admin_api_client

check_migrations_version
migrate_to_custom_group_mapper
Expand Down

0 comments on commit 5e6dddc

Please sign in to comment.