gitea: add Atlas contributor identities
Some checks failed
Tests / Declarative: Post Actions failed: 2, passed: 266
Some checks failed
Tests / Declarative: Post Actions failed: 2, passed: 266
This commit is contained in:
parent
cb1bc34f33
commit
53a090d3f5
86
services/gitea/atlas-identity-bootstrap-job.yaml
Normal file
86
services/gitea/atlas-identity-bootstrap-job.yaml
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# services/gitea/atlas-identity-bootstrap-job.yaml
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
name: gitea-atlas-identity-bootstrap-1
|
||||||
|
namespace: gitea
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: gitea
|
||||||
|
app.kubernetes.io/component: identity-bootstrap
|
||||||
|
spec:
|
||||||
|
backoffLimit: 4
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: gitea
|
||||||
|
app.kubernetes.io/component: identity-bootstrap
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
serviceAccountName: gitea-vault
|
||||||
|
securityContext:
|
||||||
|
runAsNonRoot: true
|
||||||
|
runAsUser: 1000
|
||||||
|
runAsGroup: 1000
|
||||||
|
fsGroup: 1000
|
||||||
|
seccompProfile:
|
||||||
|
type: RuntimeDefault
|
||||||
|
affinity:
|
||||||
|
podAffinity:
|
||||||
|
requiredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
- labelSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: gitea
|
||||||
|
topologyKey: kubernetes.io/hostname
|
||||||
|
nodeSelector:
|
||||||
|
node-role.kubernetes.io/worker: "true"
|
||||||
|
hardware: rpi5
|
||||||
|
containers:
|
||||||
|
- name: bootstrap
|
||||||
|
image: gitea/gitea:1.23
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- /opt/bootstrap/gitea_atlas_identity_ensure.sh
|
||||||
|
env:
|
||||||
|
- name: HOME
|
||||||
|
value: /data/git
|
||||||
|
- name: USER
|
||||||
|
value: git
|
||||||
|
- name: GITEA_WORK_DIR
|
||||||
|
value: /data/gitea
|
||||||
|
- name: GITEA_APP_INI
|
||||||
|
value: /data/gitea/conf/app.ini
|
||||||
|
- name: GITEA_API_URL
|
||||||
|
value: http://gitea.gitea.svc.cluster.local:3000/api/v1
|
||||||
|
- name: GITEA_PUBLIC_URL
|
||||||
|
value: https://scm.bstein.dev
|
||||||
|
- name: VAULT_ADDR
|
||||||
|
value: http://vault.vault.svc.cluster.local:8200
|
||||||
|
- name: VAULT_ROLE
|
||||||
|
value: gitea
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 25m
|
||||||
|
memory: 64Mi
|
||||||
|
limits:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 256Mi
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
|
- name: bootstrap
|
||||||
|
mountPath: /opt/bootstrap
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: gitea-data
|
||||||
|
- name: bootstrap
|
||||||
|
configMap:
|
||||||
|
name: gitea-atlas-identity-bootstrap
|
||||||
|
defaultMode: 0555
|
||||||
@ -9,3 +9,12 @@ resources:
|
|||||||
- deployment.yaml
|
- deployment.yaml
|
||||||
- service.yaml
|
- service.yaml
|
||||||
- ingress.yaml
|
- ingress.yaml
|
||||||
|
- atlas-identity-bootstrap-job.yaml
|
||||||
|
|
||||||
|
configMapGenerator:
|
||||||
|
- name: gitea-atlas-identity-bootstrap
|
||||||
|
files:
|
||||||
|
- gitea_atlas_identity_ensure.sh=scripts/gitea_atlas_identity_ensure.sh
|
||||||
|
|
||||||
|
generatorOptions:
|
||||||
|
disableNameSuffixHash: true
|
||||||
|
|||||||
200
services/gitea/scripts/gitea_atlas_identity_ensure.sh
Normal file
200
services/gitea/scripts/gitea_atlas_identity_ensure.sh
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
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}" "${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"
|
||||||
Loading…
x
Reference in New Issue
Block a user