- Updated README.md to reflect changes in load testing commands and added usage examples for Bash scripts. - Converted PowerShell load testing scripts to Bash equivalents for better cross-platform compatibility. - Added new Bash scripts: run-loadtests.sh, summarize-artillery.sh, and summarize-k6.sh to mirror existing PowerShell functionality. - Updated admin-api.js and ws-chat.yml for consistency and improved performance metrics reporting. - Enhanced error handling and output formatting in summary scripts for better readability.
178 lines
7.7 KiB
Bash
178 lines
7.7 KiB
Bash
#!/usr/bin/env bash
|
|
# check-tokens.sh — Verify that the credentials in the environment actually work.
|
|
# Mirrors scripts/check-tokens.ps1.
|
|
#
|
|
# Reads .env (or a supplied file) and validates access against live services:
|
|
# 1. Gitea container registry (CI_REGISTRY_TOKEN / CI_REGISTRY_USER).
|
|
# 2. Cloudflare API token(s) (CLOUDFLARE_API_TOKEN, and any R2 admin token).
|
|
# 3. R2/S3 bucket access (R2_ACCESS_KEY_ID + R2_SECRET_ACCESS_KEY) — lists buckets.
|
|
# 4. Linear Void admin login (ADMIN_USERNAME / ADMIN_PASSWORD) against the app origin.
|
|
#
|
|
# NOTE: Runs the checks through the target Docker host (default 192.168.88.40) so
|
|
# network + TLS work regardless of this machine's proxy/firewall. Uses the
|
|
# curlimages/curl and amazon/aws-cli images, which are pulled on demand.
|
|
#
|
|
# Usage:
|
|
# ./scripts/check-tokens.sh
|
|
# ./scripts/check-tokens.sh -e .env
|
|
# ./scripts/check-tokens.sh -t 192.168.88.40
|
|
#
|
|
# Exit code 0 = all credentials valid; 1 = at least one check failed.
|
|
set -uo pipefail
|
|
|
|
ENV_FILE=""
|
|
TARGET=192.168.88.40
|
|
DOCKER_PORT=2375
|
|
TIMEOUT=20
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
-e|--env-file) ENV_FILE="$2"; shift 2;;
|
|
-t|--target) TARGET="$2"; shift 2;;
|
|
-p|--docker-port) DOCKER_PORT="$2"; shift 2;;
|
|
--timeout) TIMEOUT="$2"; shift 2;;
|
|
*) echo "Unknown option: $1" >&2; exit 2;;
|
|
esac
|
|
done
|
|
|
|
export DOCKER_HOST="tcp://${TARGET}:${DOCKER_PORT}"
|
|
|
|
FAILED=0
|
|
fail() { printf ' [FAIL] %s\n' "$1"; FAILED=1; }
|
|
ok() { printf ' [OK] %s\n' "$1"; }
|
|
warn() { printf ' [WARN] %s\n' "$1"; }
|
|
|
|
# Resolve env file: explicit, or $PWD/.env, or repo root.
|
|
if [[ -z "$ENV_FILE" ]]; then
|
|
for cand in "$PWD/.env" "$(dirname "$PWD")/.env"; do
|
|
if [[ -f "$cand" ]]; then ENV_FILE="$cand"; break; fi
|
|
done
|
|
fi
|
|
if [[ -z "$ENV_FILE" || ! -f "$ENV_FILE" ]]; then
|
|
echo "No .env file found. Pass one with -e." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Parse KEY=VALUE lines into an associative array.
|
|
declare -A envs
|
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
if [[ "$line" =~ ^[[:space:]]*([A-Za-z0-9_]+)[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$ ]]; then
|
|
envs["${BASH_REMATCH[1]}"]="${BASH_REMATCH[2]}"
|
|
fi
|
|
done < "$ENV_FILE"
|
|
|
|
get_val() { [[ -n "${envs[$1]+x}" ]] && echo "${envs[$1]}"; }
|
|
|
|
# Pull helper images once.
|
|
echo "Preparing helper images (curl, aws-cli) on $TARGET..."
|
|
docker pull curlimages/curl:latest >/dev/null 2>&1 || true
|
|
docker pull amazon/aws-cli:latest >/dev/null 2>&1 || true
|
|
|
|
# dc_curl URL [bearer] [token] — run curl via the curlimages/curl container.
|
|
dc_curl() {
|
|
local url="$1" bearer="${2:-}" token="${3:-}"
|
|
local -a args=(run --rm curlimages/curl:latest -sS --max-time "$TIMEOUT")
|
|
[[ -n "$bearer" ]] && args+=( -H "Authorization: Bearer $bearer" )
|
|
[[ -n "$token" ]] && args+=( -H "Authorization: token $token" )
|
|
args+=( "$url" )
|
|
docker "${args[@]}" 2>&1 | tr -d '\r' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
echo "== Linear Void credential / token check =="
|
|
echo "Env file : $ENV_FILE"
|
|
echo "Docker host: $DOCKER_HOST"
|
|
|
|
# --- 1. Gitea registry token ---
|
|
echo ""
|
|
echo "-- Gitea container registry --"
|
|
reg_user="$(get_val CI_REGISTRY_USER)"
|
|
reg_token="$(get_val CI_REGISTRY_TOKEN)"
|
|
if [[ -n "$reg_user" && -n "$reg_token" ]]; then
|
|
# Non-sensitive probe: /api/v1/user returns a scope message for token-based creds.
|
|
out="$(dc_curl 'https://git.allucanget.biz/api/v1/user' "" "$reg_token")"
|
|
if [[ "$out" =~ token.does.not.have.at.least.one.of.required.scope || "$out" =~ '"login"' || "$out" =~ invalid_grant ]]; then
|
|
if [[ "$out" =~ invalid_grant|Unauthorized|invalid ]]; then
|
|
fail "Gitea registry token rejected ($reg_user): $out"
|
|
else
|
|
ok "Gitea registry token valid for '$reg_user' (scope: write:package/write:repository)"
|
|
fi
|
|
else
|
|
warn "Gitea registry token response unexpected: $out"
|
|
fi
|
|
else
|
|
warn "CI_REGISTRY_USER / CI_REGISTRY_TOKEN not set; skipping Gitea registry check"
|
|
fi
|
|
|
|
# --- 2. Cloudflare API token ---
|
|
echo ""
|
|
echo "-- Cloudflare API tokens --"
|
|
cf_token="$(get_val CLOUDFLARE_API_TOKEN)"
|
|
if [[ -n "$cf_token" ]]; then
|
|
out="$(dc_curl 'https://api.cloudflare.com/client/v4/user/tokens/verify' "$cf_token")"
|
|
if [[ "$out" =~ '"success":true' ]]; then
|
|
if [[ "$out" =~ '"status":"active"' ]]; then ok 'Cloudflare API token valid and ACTIVE'; else warn "Cloudflare API token valid but status: $out"; fi
|
|
else
|
|
fail "Cloudflare API token rejected: $out"
|
|
fi
|
|
else
|
|
warn "CLOUDFLARE_API_TOKEN not set; skipping Cloudflare check"
|
|
fi
|
|
|
|
# --- 3. Cloudflare R2 / S3 bucket access ---
|
|
echo ""
|
|
echo "-- Cloudflare R2 (S3-compatible) --"
|
|
r2_key="$(get_val R2_ACCESS_KEY_ID)"
|
|
r2_secret="$(get_val R2_SECRET_ACCESS_KEY)"
|
|
r2_ep="$(get_val R2_ENDPOINT)"
|
|
bucket="$(get_val R2_BUCKET_NAME)"
|
|
|
|
# Use the admin key pair (which has ListBuckets) when present, else the regular key.
|
|
list_k="$(get_val R2_ADMIN_ACCESS_KEY_ID)"; list_s="$(get_val R2_ADMIN_SECRET_ACCESS_KEY)"
|
|
if [[ -z "$list_k" || -z "$list_s" ]]; then list_k="$r2_key"; list_s="$r2_secret"; fi
|
|
|
|
if [[ -n "$list_k" && -n "$list_s" && -n "$r2_ep" ]]; then
|
|
out="$(docker run --rm --entrypoint sh amazon/aws-cli:latest -c "export AWS_ACCESS_KEY_ID='$list_k'; export AWS_SECRET_ACCESS_KEY='$list_s'; aws s3 ls --endpoint-url '$r2_ep' --region auto 2>&1" 2>&1)"
|
|
if grep -q "$bucket" <<<"$out"; then ok "R2 bucket listing OK, '$bucket' present"
|
|
elif grep -q 'AccessDenied' <<<"$out"; then fail "R2 listing denied (no ListBuckets permission): $out"
|
|
elif grep -qE '^[^[:space:]]+[[:space:]]+' <<<"$out"; then warn "R2 listing returned (unexpected): $out"
|
|
else fail "R2 access failed: $out"; fi
|
|
else
|
|
warn "R2 credentials / endpoint not fully set; skipping R2 check"
|
|
fi
|
|
|
|
# Verify the specific application bucket is reachable with the primary key (read).
|
|
if [[ -n "$r2_key" && -n "$r2_secret" && -n "$r2_ep" && -n "$bucket" ]]; then
|
|
out="$(docker run --rm --entrypoint sh amazon/aws-cli:latest -c "export AWS_ACCESS_KEY_ID='$r2_key'; export AWS_SECRET_ACCESS_KEY='$r2_secret'; aws s3 ls s3://$bucket/ --endpoint-url '$r2_ep' --region auto --max-keys 5 2>&1 | head -5" 2>&1)"
|
|
if grep -q 'AccessDenied' <<<"$out"; then fail "Bucket '$bucket' read denied with primary key"
|
|
elif [[ -z "$out" ]]; then ok "Bucket '$bucket' reachable (empty listing, expected)"
|
|
elif grep -q 'NoSuchBucket' <<<"$out"; then fail "Bucket '$bucket' NOT FOUND"
|
|
else ok "Bucket '$bucket' reachable"; fi
|
|
elif [[ -z "$bucket" ]]; then
|
|
warn "R2_BUCKET_NAME not set; skipping bucket-level check"
|
|
fi
|
|
|
|
# --- 4. Linear Void admin login ---
|
|
echo ""
|
|
echo "-- Linear Void admin login --"
|
|
admin_user="$(get_val ADMIN_USERNAME)"
|
|
admin_pass="$(get_val ADMIN_PASSWORD)"
|
|
if [[ -n "$admin_user" && -n "$admin_pass" ]]; then
|
|
# Hit the chat backend directly (public origin is caught by nginx SPA fallback).
|
|
url="http://${TARGET}:3000/api/admin/login"
|
|
body="{\"username\":\"$admin_user\",\"password\":\"$admin_pass\"}"
|
|
wrong_body="{\"username\":\"$admin_user\",\"password\":\"__definitely_wrong__\"}"
|
|
ok_code="$(docker run --rm curlimages/curl:latest -sS --max-time "$TIMEOUT" -o /dev/null -w '%{http_code}' -X POST -H 'Content-Type: application/json' -d "$body" "$url" 2>/dev/null | tr -d '[:space:]')"
|
|
wrong_code="$(docker run --rm curlimages/curl:latest -sS --max-time "$TIMEOUT" -o /dev/null -w '%{http_code}' -X POST -H 'Content-Type: application/json' -d "$wrong_body" "$url" 2>/dev/null | tr -d '[:space:]')"
|
|
if [[ "$ok_code" == "200" && "$wrong_code" == "401" ]]; then ok "Admin credentials valid (correct=200, wrong=401)"
|
|
else fail "Admin login unexpected (correct=$ok_code, wrong=$wrong_code)"; fi
|
|
else
|
|
warn "ADMIN_USERNAME / ADMIN_PASSWORD not set; skipping admin check"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
echo ""
|
|
if [[ "$FAILED" -ne 0 ]]; then echo "RESULT: FAILED"; exit 1; fi
|
|
echo "RESULT: OK (no failing checks)"
|
|
exit 0
|