titan-iac/scripts/jenkins_cred_sync.sh

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 "$@"