atlas-iac/services/gitea/scripts/gitea_atlas_identity_ensure.sh
2026-08-13 19:43:33 -03:00

202 lines
6.5 KiB
Bash

#!/bin/sh
set -eu
app_ini="${GITEA_APP_INI:-/data/gitea/conf/app.ini}"
work_dir="${GITEA_WORK_DIR:-/data/gitea}"
api_url="${GITEA_API_URL:-http://gitea.gitea.svc.cluster.local:3000/api/v1}"
public_url="${GITEA_PUBLIC_URL:-https://scm.bstein.dev}"
vault_addr="${VAULT_ADDR:-http://vault.vault.svc.cluster.local:8200}"
vault_role="${VAULT_ROLE:-gitea}"
service_account_token=/var/run/secrets/kubernetes.io/serviceaccount/token
reconciler_user=atlas-reconciler
reconciler_email=atlas-reconciler@bstein.dev
hermes_user=hermes-automation
hermes_email=hermes-automation@bstein.dev
organization=atlas
owner_user=bstein
contributor_team=Contributors
contributor_units='["repo.actions","repo.packages","repo.code","repo.issues","repo.wiki","repo.pulls","repo.releases","repo.projects"]'
die() {
echo "Gitea Atlas identity bootstrap failed: $*" >&2
exit 1
}
gitea_admin() {
/usr/local/bin/gitea --work-path "${work_dir}" --config "${app_ini}" admin "$@"
}
wait_for_gitea() {
attempt=0
while [ "${attempt}" -lt 120 ]; do
if [ -r "${app_ini}" ] && curl --fail --silent --show-error "${api_url}/version" >/dev/null 2>&1; then
return 0
fi
attempt=$((attempt + 1))
sleep 2
done
die "Gitea did not become ready"
}
user_exists() {
wanted_user=$1
gitea_admin user list 2>/dev/null | awk 'NR > 1 { print $2 }' | grep -Fxq "${wanted_user}"
}
ensure_user() {
username=$1
email=$2
admin_flag=$3
if user_exists "${username}"; then
return 0
fi
if [ "${admin_flag}" = true ]; then
gitea_admin user create \
--username "${username}" \
--email "${email}" \
--random-password \
--must-change-password=false \
--admin >/dev/null
else
gitea_admin user create \
--username "${username}" \
--email "${email}" \
--random-password \
--must-change-password=false >/dev/null
fi
}
generate_token() {
username=$1
token_name="atlas-bootstrap-$(date +%s)-${username}"
gitea_admin user generate-access-token \
--username "${username}" \
--token-name "${token_name}" \
--scopes all \
--raw 2>/dev/null
}
api_request() {
method=$1
path=$2
data=${3:-}
response_file=$4
if [ -n "${data}" ]; then
status=$(curl --silent --show-error \
--output "${response_file}" \
--write-out '%{http_code}' \
--request "${method}" \
--header "Authorization: token ${reconciler_token}" \
--header 'Content-Type: application/json' \
--data "${data}" \
"${api_url}${path}")
else
status=$(curl --silent --show-error \
--output "${response_file}" \
--write-out '%{http_code}' \
--request "${method}" \
--header "Authorization: token ${reconciler_token}" \
"${api_url}${path}")
fi
printf '%s' "${status}"
}
expect_status() {
actual=$1
expected=$2
description=$3
[ "${actual}" = "${expected}" ] || die "${description} returned HTTP ${actual}"
}
find_team_id() {
team_name=$1
teams_file=$(mktemp)
status=$(api_request GET "/orgs/${organization}/teams" '' "${teams_file}")
expect_status "${status}" 200 "list organization teams"
team_id=$(sed 's/},{/}\
{/g' "${teams_file}" | grep "\"name\":\"${team_name}\"" | head -n 1 | grep -o '"id":[0-9]*' | head -n 1 | cut -d: -f2 || true)
rm -f "${teams_file}"
[ -n "${team_id}" ] || die "team ${team_name} was not found"
printf '%s' "${team_id}"
}
vault_login() {
[ -r "${service_account_token}" ] || die "Kubernetes service account token is unavailable"
jwt=$(cat "${service_account_token}")
login_file=$(mktemp)
status=$(curl --silent --show-error \
--output "${login_file}" \
--write-out '%{http_code}' \
--request POST \
--header 'Content-Type: application/json' \
--data "{\"role\":\"${vault_role}\",\"jwt\":\"${jwt}\"}" \
"${vault_addr}/v1/auth/kubernetes/login")
expect_status "${status}" 200 "Vault Kubernetes login"
vault_token=$(sed -n 's/.*"client_token":"\([^"]*\)".*/\1/p' "${login_file}")
rm -f "${login_file}"
[ -n "${vault_token}" ] || die "Vault login did not return a client token"
}
vault_write() {
path=$1
payload=$2
response_file=$(mktemp)
status=$(curl --silent --show-error \
--output "${response_file}" \
--write-out '%{http_code}' \
--request POST \
--header "X-Vault-Token: ${vault_token}" \
--header 'Content-Type: application/json' \
--data "${payload}" \
"${vault_addr}/v1/kv/data/atlas/${path}")
rm -f "${response_file}"
expect_status "${status}" 200 "write Vault path ${path}"
}
wait_for_gitea
ensure_user "${reconciler_user}" "${reconciler_email}" true
ensure_user "${hermes_user}" "${hermes_email}" false
reconciler_token=$(generate_token "${reconciler_user}")
hermes_token=$(generate_token "${hermes_user}")
[ -n "${reconciler_token}" ] || die "reconciler token generation failed"
[ -n "${hermes_token}" ] || die "Hermes token generation failed"
response_file=$(mktemp)
status=$(api_request POST /orgs "{\"username\":\"${organization}\",\"full_name\":\"Atlas\",\"description\":\"Atlas platform services\",\"visibility\":\"private\",\"repo_admin_change_team_access\":true}" "${response_file}")
rm -f "${response_file}"
case "${status}" in
201|422) ;;
*) die "create organization returned HTTP ${status}" ;;
esac
owners_id=$(find_team_id Owners)
response_file=$(mktemp)
status=$(api_request PUT "/teams/${owners_id}/members/${owner_user}" '' "${response_file}")
rm -f "${response_file}"
expect_status "${status}" 204 "add ${owner_user} to Atlas owners"
response_file=$(mktemp)
status=$(api_request POST "/orgs/${organization}/teams" "{\"name\":\"${contributor_team}\",\"description\":\"Atlas automation contributors\",\"permission\":\"write\",\"includes_all_repositories\":true,\"can_create_org_repo\":false,\"units\":${contributor_units}}" "${response_file}")
rm -f "${response_file}"
case "${status}" in
201|422) ;;
*) die "create contributor team returned HTTP ${status}" ;;
esac
contributors_id=$(find_team_id "${contributor_team}")
response_file=$(mktemp)
status=$(api_request PUT "/teams/${contributors_id}/members/${hermes_user}" '' "${response_file}")
rm -f "${response_file}"
expect_status "${status}" 204 "add Hermes to Atlas contributors"
vault_login
vault_write gitea/atlas-reconciler "{\"data\":{\"username\":\"${reconciler_user}\",\"token\":\"${reconciler_token}\",\"base_url\":\"${public_url}\"}}"
vault_write hermes/developer-gitea "{\"data\":{\"username\":\"${hermes_user}\",\"email\":\"${hermes_email}\",\"token\":\"${hermes_token}\",\"base_url\":\"${public_url}\"}}"
echo "Atlas organization and Hermes contributor identity are ready"