95 lines
3.2 KiB
Bash
Executable File
95 lines
3.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Sync Keycloak users into Jenkins local accounts (for CLI/API use).
|
|
# Jenkins is OIDC-enabled, but local users can still be provisioned for tokens.
|
|
# Requires: curl, jq, kubectl. Needs Jenkins admin user+API token.
|
|
|
|
set -euo pipefail
|
|
|
|
require() { command -v "$1" >/dev/null 2>&1 || { echo "missing required binary: $1" >&2; exit 1; }; }
|
|
require curl; require jq; require kubectl
|
|
|
|
: "${KEYCLOAK_URL:=https://sso.bstein.dev}"
|
|
: "${KEYCLOAK_REALM:=atlas}"
|
|
: "${KEYCLOAK_CLIENT_ID:?set KEYCLOAK_CLIENT_ID or export via secret}"
|
|
: "${KEYCLOAK_CLIENT_SECRET:?set KEYCLOAK_CLIENT_SECRET or export via secret}"
|
|
: "${JENKINS_URL:=https://ci.bstein.dev}"
|
|
: "${JENKINS_NAMESPACE:=jenkins}"
|
|
: "${JENKINS_ADMIN_SECRET_NAME:=jenkins-admin-token}"
|
|
: "${JENKINS_ADMIN_USER_KEY:=username}"
|
|
: "${JENKINS_ADMIN_TOKEN_KEY:=token}"
|
|
: "${DEFAULT_PASSWORD:=TempSsoPass!2025}"
|
|
|
|
fetch_token() {
|
|
curl -fsS -X POST \
|
|
-d "grant_type=client_credentials" \
|
|
-d "client_id=${KEYCLOAK_CLIENT_ID}" \
|
|
-d "client_secret=${KEYCLOAK_CLIENT_SECRET}" \
|
|
"${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}/protocol/openid-connect/token" \
|
|
| jq -r '.access_token'
|
|
}
|
|
|
|
pull_users() {
|
|
local token="$1"
|
|
curl -fsS -H "Authorization: Bearer ${token}" \
|
|
"${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?max=500" \
|
|
| jq -r '.[] | select(.enabled == true) | select(.username | startswith("service-account-") | not) | [.id, .username, (.email // "")] | @tsv'
|
|
}
|
|
|
|
get_admin_auth() {
|
|
local user token
|
|
if [[ -n "${JENKINS_ADMIN_USER:-}" && -n "${JENKINS_ADMIN_TOKEN:-}" ]]; then
|
|
echo "${JENKINS_ADMIN_USER}:${JENKINS_ADMIN_TOKEN}"
|
|
return
|
|
fi
|
|
user=$(kubectl -n "${JENKINS_NAMESPACE}" get secret "${JENKINS_ADMIN_SECRET_NAME}" -o "jsonpath={.data.${JENKINS_ADMIN_USER_KEY}}" | base64 -d)
|
|
token=$(kubectl -n "${JENKINS_NAMESPACE}" get secret "${JENKINS_ADMIN_SECRET_NAME}" -o "jsonpath={.data.${JENKINS_ADMIN_TOKEN_KEY}}" | base64 -d)
|
|
echo "${user}:${token}"
|
|
}
|
|
|
|
get_crumb() {
|
|
local auth="$1"
|
|
curl -fsS -u "${auth}" "${JENKINS_URL}/crumbIssuer/api/json" | jq -r .crumb
|
|
}
|
|
|
|
user_exists() {
|
|
local auth="$1" user="$2"
|
|
local code
|
|
code=$(curl -s -o /dev/null -w '%{http_code}' -u "${auth}" "${JENKINS_URL}/user/${user}/api/json")
|
|
[[ "${code}" == "200" ]]
|
|
}
|
|
|
|
create_user() {
|
|
local auth="$1" crumb="$2" username="$3" email="$4"
|
|
local status
|
|
status=$(curl -s -o /dev/null -w '%{http_code}' \
|
|
-u "${auth}" \
|
|
-H "Jenkins-Crumb: ${crumb}" \
|
|
-X POST \
|
|
--data "username=${username}&password1=${DEFAULT_PASSWORD}&password2=${DEFAULT_PASSWORD}&fullname=${username}&email=${email}" \
|
|
"${JENKINS_URL}/securityRealm/createAccountByAdmin")
|
|
|
|
if [[ "${status}" == "200" || "${status}" == "302" ]]; then
|
|
echo "created jenkins user ${username}"
|
|
elif [[ "${status}" == "400" ]]; then
|
|
echo "jenkins user ${username} already exists (400)" >&2
|
|
else
|
|
echo "failed to create jenkins user ${username} (status ${status})" >&2
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
local kc_token auth crumb
|
|
kc_token="$(fetch_token)"
|
|
auth="$(get_admin_auth)"
|
|
crumb="$(get_crumb "${auth}")"
|
|
|
|
while IFS=$'\t' read -r _ uid email; do
|
|
if user_exists "${auth}" "${uid}"; then
|
|
continue
|
|
fi
|
|
create_user "${auth}" "${crumb}" "${uid}" "${email}"
|
|
done < <(pull_users "${kc_token}")
|
|
}
|
|
|
|
main "$@"
|