Skip to content

Commit

Permalink
Enhance host in Keycloak valid redirects list and TLS cert (#3503)
Browse files Browse the repository at this point in the history
Include 'alternate names' for all of the server's hostnames and IP addresses
in the TLS certificate and use a similar list for the Keycloak valid redirects.

Ensure that the Keycloak realm and client in the Pbench Server configuration
file match the configuration of the canned Keycloak server, and make the value
of the Keycloak server in the Pbench Server config file be configurable.

PBENCH-1227
  • Loading branch information
webbnh authored Aug 4, 2023
1 parent c3688cd commit b2e56ac
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 24 deletions.
4 changes: 3 additions & 1 deletion server/pbenchinacan/etc/pbench-server/pbench-server.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ uri = postgresql://pbenchcontainer:pbench@localhost:5432/pbenchcontainer
secret-key = "pbench-in-a-can secret shhh"

[openid]
server_url = https://localhost:8090
server_url = ##KEYCLOAK_SERVER_URL##
realm = ##KEYCLOAK_REALM##
client = ##KEYCLOAK_CLIENT##

# Provide a CA cert for the pbenchinacan Keycloak server connection.
tls_ca_file = /etc/pki/tls/certs/pbench_CA.crt
Expand Down
34 changes: 23 additions & 11 deletions server/pbenchinacan/load_keycloak.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@

# The script defaults to using master realm username/password as admin/admin
# unless specified otherwise by 'ADMIN_USERNAME' and 'ADMIN_PASSWORD' env
# variables. The script also defaults the keycloak redirect URI as
# "https://localhost:8443/*" unless specified otherwise by 'KEYCLOAK_REDIRECT_URI'
# env variable.
# variables. The script constructs the Keycloak redirect URI list as
# "https://<node>:8443/*" from the space-separated list of nodes in
# the 'KEYCLOAK_REDIRECT_HOSTS' env variable, defaulting to 'localhost' plus
# whatever we can glean from the 'hostname' command. It adds to that list the
# value of 'KEYCLOAK_DEV_REDIRECT' which defaults to "http://localhost:3000/*".

KEYCLOAK_HOST_PORT=${KEYCLOAK_HOST_PORT:-"https://localhost:8090"}
KEYCLOAK_REDIRECT_URI=${KEYCLOAK_REDIRECT_URI:-"https://localhost:8443/*"}
KEYCLOAK_REDIRECT_HOSTS=${KEYCLOAK_REDIRECT_HOSTS:-"localhost $(hostname -A) $(hostname -f) 127.0.0.1 ::1 $(hostname -I)"}
KEYCLOAK_DEV_REDIRECT=${KEYCLOAK_DEV_REDIRECT:-"http://localhost:3000/*"}
ADMIN_USERNAME=${ADMIN_USERNAME:-"admin"}
ADMIN_PASSWORD=${ADMIN_PASSWORD:-"admin"}
Expand All @@ -34,6 +36,16 @@ export CURL_CA_BUNDLE=${CURL_CA_BUNDLE:-"${PWD}/server/pbenchinacan/etc/pki/tls/

end_in_epoch_secs=$(date --date "2 minutes" +%s)

keycloak_redirect_uris="\"${KEYCLOAK_DEV_REDIRECT}\""
for n in ${KEYCLOAK_REDIRECT_HOSTS}; do
if [[ $n =~ ^[0-9a-f]{0,4}(:[0-9a-f]{0,4}){1,8}$ ]]; then
n="[$n]" # IPv6 addresses must be wrapped in square-brackets
fi
keycloak_redirect_uris="${keycloak_redirect_uris}, \"https://${n}:8443/*\""
done

echo "Keycloak redirect URI list is <${keycloak_redirect_uris}>."

# Run the custom configuration

ADMIN_TOKEN=""
Expand Down Expand Up @@ -64,7 +76,7 @@ status_code=$(curl -f -s -o /dev/null -w "%{http_code}" -X POST \
"${KEYCLOAK_HOST_PORT}/admin/realms" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"realm": "'${REALM}'", "enabled": true}')
-d '{"realm": "'"${REALM}"'", "enabled": true}')

if [[ "${status_code}" != "201" ]]; then
echo "Realm creation failed with ${status_code}"
Expand Down Expand Up @@ -99,7 +111,7 @@ curl -si -f -X POST \
"protocolMapper": "oidc-audience-mapper",
"consentRequired": false,
"config": {
"included.client.audience": "'${CLIENT}'",
"included.client.audience": "'"${CLIENT}"'",
"id.token.claim": "false",
"access.token.claim": "true"
}
Expand All @@ -111,16 +123,16 @@ CLIENT_CONF=$(curl -si -f -X POST \
"${KEYCLOAK_HOST_PORT}/admin/realms/${REALM}/clients" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"clientId": "'${CLIENT}'",
-d '{"clientId": "'"${CLIENT}"'",
"publicClient": true,
"defaultClientScopes": ["pbench", "openid", "profile", "email"],
"directAccessGrantsEnabled": true,
"serviceAccountsEnabled": true,
"enabled": true,
"attributes": {"post.logout.redirect.uris": "+"},
"redirectUris": ["'${KEYCLOAK_REDIRECT_URI}'", "'${KEYCLOAK_DEV_REDIRECT}'"]}')
"redirectUris": ['"${keycloak_redirect_uris}"']}')

CLIENT_ID=$(grep -o -e 'https://[^[:space:]]*' <<< ${CLIENT_CONF} | sed -e 's|.*/||')
CLIENT_ID=$(grep -o -e 'https://[^[:space:]]*' <<< "${CLIENT_CONF}" | sed -e 's|.*/||')
if [[ -z "${CLIENT_ID}" ]]; then
echo "${CLIENT} id is empty"
exit 1
Expand Down Expand Up @@ -155,7 +167,7 @@ USER=$(curl -si -f -X POST \
-H "Content-Type: application/json" \
-d '{"username": "admin", "enabled": true, "credentials": [{"type": "password", "value": "123", "temporary": false}]}')

USER_ID=$(grep -o -e 'https://[^[:space:]]*' <<< ${USER} | sed -e 's|.*/||')
USER_ID=$(grep -o -e 'https://[^[:space:]]*' <<< "${USER}" | sed -e 's|.*/||')

if [[ -z "${USER_ID}" ]]; then
echo "User id is empty"
Expand All @@ -168,7 +180,7 @@ status_code=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
"${KEYCLOAK_HOST_PORT}/admin/realms/${REALM}/users/${USER_ID}/role-mappings/clients/${CLIENT_ID}" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '[{"id":"'${ROLE_ID}'","name":"ADMIN"}]')
-d '[{"id":"'"${ROLE_ID}"'","name":"ADMIN"}]')

if [[ "${status_code}" != "204" ]]; then
echo "Assigning 'ADMIN' client role to the user 'admin' failed with ${status_code}"
Expand Down
47 changes: 35 additions & 12 deletions server/pbenchinacan/run-pbench-in-a-can
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,41 @@ export PB_SERVER_IMAGE_PULL_POLICY="${PB_SERVER_IMAGE_PULL_POLICY:-${PB_COMMON_I
# Directory to use for the fully built dashboard code.
export PB_DASHBOARD_DIR="${PB_DASHBOARD_DIR:-${PWD}/dashboard/build/}"

# Keycloak realm and client IDs to be used by the load_keycloak.sh script and
# the pbench-server.cfg file.
export KEYCLOAK_REALM=${KEYCLOAK_REALM:-"pbench-server"}
export KEYCLOAK_CLIENT=${KEYCLOAK_CLIENT:-"pbench-client"}

# Note: the value of PB_HOST_IP will be used to generate the TLS certificate
# and so it (not `localhost`) must also be used to access the Pbench Server;
# otherwise, the TLS validation will fail due to a host mismatch.
if [[ -z "${PB_HOST_IP}" ]]; then
host_ip_list=$(hostname -I)
PB_HOST_IP=${host_ip_list%% *}
export PB_HOST_IP
fi
# Name or IP address to be used by the client to access the Pbench Server, to
# load the Dashboard, and to interface with the canned Keycloak server
host_name=${PB_HOST:-localhost}

host_name=${PB_HOST_NAME:-$(hostname --fqdn)}
# Set a value for the -addext "subjectAltName=..." option to the cert creation.
# Set the initial value "manually" and skip the "dummy" value to make the
# comma-separated concatenations work nicely.
#
# Notes about the use of `readarray`: in order for the output array to be
# visible to the rest of the commands, the `readarray` command must not be
# executed in a subprocess, and therefore it cannot be on the receiving end
# of a pipe. Instead we execute the `hostname` command in a "command
# expansion" inside a "here string". Unfortunately, this causes a newline
# to be appended to the output, so that if we divide the result on spaces
# we end up with an extra array entry containing just a newline. So, instead
# we translate the spaces into newlines ("squeezing" out any repeated
# delimiters) and use the `readarray` default delimiter which is the newline.
subj_alt_name="DNS.1:localhost"
readarray -t cert_hostnames <<< "$(echo "dummy $(hostname -A)" | tr -s ' ' '\n')"
for ((i=1; i < ${#cert_hostnames[*]}; i++)); do
subj_alt_name+=", DNS.$((i+1)):${cert_hostnames[i]}"
done
readarray -t cert_ipaddrs <<< "$(echo "127.0.0.1 ::1 $(hostname -I)" | tr -s ' ' '\n')"
for ((i=0; i < ${#cert_ipaddrs[*]}; i++)); do
subj_alt_name+=", IP.$((i+1)):${cert_ipaddrs[i]}"
done
echo "subjectAltName is <${subj_alt_name}>."

grep -q ${host_name} <<< ${subj_alt_name} \
|| echo "Warning: requested host name/addr (${host_name}) is not covered by the TLS cert." >&2

# Set up TMP_DIR, if it's not already defined, to point to WORKSPACE_TMP, if it
# is defined (e.g., by the CI), or to `/var/tmp/pbench` as a fallback.
Expand Down Expand Up @@ -65,8 +87,9 @@ cp ${pbiac_etc}/pbench-server/pbench-server.cfg ${PB_DEPLOY_FILES}/
# Customize the Pbench Server config file for canned operation
sed -Ei \
-e "/^ *realhost/ s/=.*/= $(hostname -f)/" \
-e "s/<keycloak_realm>/${KEYCLOAK_REALM}/" \
-e "s/<keycloak_client>/${KEYCLOAK_CLIENT}/" \
-e "s|##KEYCLOAK_SERVER_URL##|https://${host_name}:8090|" \
-e "s/##KEYCLOAK_REALM##/${KEYCLOAK_REALM}/" \
-e "s/##KEYCLOAK_CLIENT##/${KEYCLOAK_CLIENT}/" \
-e "s/##ADMIN_NAMES##/${PB_ADMIN_NAMES}/" \
${PB_DEPLOY_FILES}/pbench-server.cfg

Expand Down Expand Up @@ -132,7 +155,7 @@ podman run \
-addext "authorityKeyIdentifier = keyid,issuer" \
-addext "basicConstraints=CA:FALSE" \
-addext "keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment" \
-addext "subjectAltName = IP.2:${PB_HOST_IP}, DNS:localhost" \
-addext "subjectAltName = ${subj_alt_name}" \
2>&1 | sed -E -e '/^[.+*-]*$/ d'

chmod 0640 ${PB_DEPLOY_FILES}/pbench-server.key
Expand Down

0 comments on commit b2e56ac

Please sign in to comment.