skills: agent-provisioning - codified identity stand-up workflow

Policy rulings (#942): unique passwords, Cloudron-only SSO accounts,
no reachableceo credentials, TOTP seeds MUST be captured to the vault.
Cloudron admin API wrapper, cookie-jar OIDC login, RFC-6238-tested
TOTP helper, per-system SSO/API references, incident log.
This commit is contained in:
TSYS Group COO
2026-09-08 04:07:14 -05:00
parent a4a54f553e
commit bde7845490
8 changed files with 503 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
# Authenticated Cloudron admin API call (compliant lever: api-token service
# account from vault item creds/cloudron — never the founder's accounts).
# usage: cloudron-api.sh METHOD /api/v1/PATH [JSON-BODY]
# body -> stdout; HTTP status -> stderr. Secrets never printed.
set -euo pipefail
BASE="https://my.knownelement.com"
METHOD="${1:?usage: cloudron-api.sh METHOD /api/v1/PATH [JSON-BODY]}"
PATHPART="${2:?usage: cloudron-api.sh METHOD /api/v1/PATH [JSON-BODY]}"
BODY="${3:-}"
if [ "$(id -un)" = "TSGCOO" ]; then
SM() { /data2/TSGCOO/.local/bin/sm "$@" </dev/null; }
else
SM() { sudo -u TSGCOO /data2/TSGCOO/.local/bin/sm "$@" </dev/null; }
fi
TOKEN=$(SM get creds/cloudron --field password)
[ -n "$TOKEN" ] || { echo "no cloudron token in vault" >&2; exit 1; }
ARGS=(-sk -X "$METHOD" "$BASE$PATHPART"
-H "Authorization: Bearer $TOKEN"
-H 'Content-Type: application/json'
-w '\nHTTP %{http_code}\n')
[ -n "$BODY" ] && ARGS+=(-d "$BODY")
curl "${ARGS[@]}" >&2
+59
View File
@@ -0,0 +1,59 @@
#!/bin/bash
# Full SSO login for an identity into one of the four systems, via cookie jar.
# usage: oidc-login.sh <glpi|gitea|discourse|redmine> <login>
# Reads the Cloudron password from vault item "<login> Cloudron" (env login
# triple; USERNAME may be the email form). Success leaves cookie jar at
# /tmp/oidc-<login>-<system>.txt and prints the landing URL.
# TOTP-enforced identity? Set TOTP_CODE=$(scripts/totp.sh <seed>) first.
set -euo pipefail
SYS="${1:?usage: oidc-login.sh <glpi|gitea|discourse|redmine> <login>}"
LOGIN="${2:?usage: oidc-login.sh <glpi|gitea|discourse|redmine> <login>}"
case "$SYS" in
glpi) START="https://cmdb.knownelement.com/plugins/singlesignon/front/callback.php/provider/1?remember=1"; APP="https://cmdb.knownelement.com";;
gitea) START="https://git.knownelement.com/user/oauth2/cloudron"; APP="https://git.knownelement.com";;
discourse) START="https://community.turnsys.com/auth/openid_connect"; APP="https://community.turnsys.com";;
redmine) START="https://projects.knownelement.com/oauth"; APP="https://projects.knownelement.com";;
*) echo "unknown system: $SYS" >&2; exit 2;;
esac
if [ "$(id -un)" = "TSGCOO" ]; then
SM() { /data2/TSGCOO/.local/bin/sm "$@" </dev/null; }
else
SM() { sudo -u TSGCOO /data2/TSGCOO/.local/bin/sm "$@" </dev/null; }
fi
eval "$(SM env "$LOGIN Cloudron")"
[ -n "${PASSWORD:-}" ] || { echo "vault item '$LOGIN Cloudron' empty" >&2; exit 1; }
USER_FIELD="${USERNAME:-$LOGIN}"
JAR="/tmp/oidc-${LOGIN}-${SYS}.txt"; rm -f "$JAR"
# 1. Start the SSO flow; follow redirects to the OIDC interaction page.
INTURL=$(curl -s -o /dev/null -w '%{redirect_url}' -c "$JAR" "$START")
case "$INTURL" in
https://my.knownelement.com/openid/interaction/*) : ;;
*) echo "no interaction redirect (got: '${INTURL:-none}'). If this is redmine, see references/systems.md (known bounce). If discourse, the route may need CSRF via a real session." >&2; exit 3;;
esac
# 2. Interaction login. Field name is `username` (NOT `login`).
BODY="{\"username\":\"$USER_FIELD\",\"password\":\"$PASSWORD\""
if [ -n "${TOTP_CODE:-}" ]; then BODY="$BODY,\"totpToken\":\"$TOTP_CODE\""; fi
BODY="$BODY}"
RESP=$(curl -s -b "$JAR" -c "$JAR" -X POST "${INTURL}/login" \
-H 'Content-Type: application/json' -d "$BODY")
unset BODY
case "$RESP" in
*redirectTo*)
RED=$(printf '%s' "$RESP" | sed -n 's/.*"redirectTo":"\([^"]*\)".*/\1/p')
case "$RED" in http*) : ;; *) RED="$APP$RED" ;; esac
FINAL=$(curl -s -L -b "$JAR" -c "$JAR" -o /dev/null -w '%{url_effective}' "$RED")
echo "logged in: cookie jar $JAR"
echo "landing: $FINAL"
echo "verify the identity before use (users/current or /api/v1/user)."
;;
*twoFactorRequired*)
echo "2FA enforced and no/invalid TOTP_CODE given." >&2
echo "If the seed is in the vault: TOTP_CODE=\$(scripts/totp.sh \$(sm get '$LOGIN Cloudron' --field totp_seed)) $0 $SYS $LOGIN" >&2
exit 4;;
*)
echo "login rejected: $RESP" >&2
exit 5;;
esac
+20
View File
@@ -0,0 +1,20 @@
#!/bin/bash
# Current TOTP code from a base32 seed (pure bash + openssl + coreutils).
# usage: totp.sh <base32-seed> [step-seconds] [digits]
set -euo pipefail
SECRET="${1:?usage: totp.sh <base32-seed> [step] [digits]}"
STEP="${2:-30}"; DIGITS="${3:-6}"
NOW="${TOTP_NOW:-$(date +%s)}" # TOTP_NOW: RFC-6238 test vector override
KEYHEX=$(printf '%s' "$SECRET" | tr -d ' =\n-' | base32 -d 2>/dev/null | od -An -tx1 | tr -d ' \n')
[ -n "$KEYHEX" ] || { echo "bad base32 seed" >&2; exit 2; }
COUNTER=$(printf '%016x' $(( NOW / STEP )))
# bash vars cannot hold null bytes - keep the counter as a FORMAT string and
# let printf emit the raw bytes straight into the pipe.
FMT=$(printf '%s' "$COUNTER" | sed 's/../\\x&/g')
# intentional: FMT emits raw counter bytes incl. NULs - the whole point
# shellcheck disable=SC2059
MAC=$(printf "$FMT" | openssl dgst -sha1 -mac HMAC -macopt "hexkey:$KEYHEX" -binary \
| od -An -tx1 | tr -d ' \n')
OFFSET=$(( 0x${MAC: -1} ))
CODE=$(( (0x${MAC:$((OFFSET*2)):8} & 0x7fffffff) % (10 ** DIGITS) ))
printf "%0${DIGITS}d\n" "$CODE"