gendesign/scripts/auth/add_user.sh
Light1YT 6883d14177
Some checks failed
CI / changes (push) Successful in 7s
CI / backend-tests (push) Has been skipped
CI / frontend-tests (push) Has been skipped
CI / changes (pull_request) Successful in 6s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
Deploy / build-backend (push) Blocked by required conditions
Deploy / build-worker (push) Blocked by required conditions
Deploy / build-frontend (push) Blocked by required conditions
Deploy / deploy (push) Blocked by required conditions
Deploy / changes (push) Has been cancelled
fix(ops): repair broken main-DB backup + harden auth scripts/docs (#71 #427 #429 #428)
#71 (CRITICAL): backup.sh committed 100644 → git reset --hard on deploy
re-asserts non-exec mode → raw-path cron fails Permission denied (last good
dump 2026-05-27, no S3). Commit 100755 + chmod ops/*.sh in deploy.yml +
size sanity-check (never prune good dumps for a truncated one) + keep-N
retention + optional S3 (redacted /etc/default template). Modeled on the
working backup-tradein-db.sh.

#427: widen basic_auth username regex ^[a-z][a-z0-9_.-]{1,62}$ + literal-escape
  dotted names in grep probes.
#429: replace list_users.sh false-positive grep with anchored awk over basic_auth block.
#428: PILOT_ACCESS.md support email → pilot@gendsgn.ru.

Closes #71
Closes #427
Closes #429
Closes #428
2026-06-13 20:13:04 +05:00

113 lines
4.7 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# add_user.sh — добавить пользователя в caddy/users.caddy.snippet
# Usage: ./scripts/auth/add_user.sh <username> "<comment>"
#
# - Читает пароль из stdin (pipe) или генерирует 16-char random если интерактивен.
# - Bcrypt через `docker run --rm caddy:2 caddy hash-password`.
# - Печатает plain password на stdout ОДИН РАЗ.
# - НЕ коммитит изменения — это задача разработчика.
set -euo pipefail
# Скрипт лежит в `<repo>/scripts/auth/`; snippet — в `<repo>/caddy/`.
# Нужно 3× `dirname`: $0 (.../scripts/auth/add_user.sh) → .../scripts/auth → .../scripts → .../<repo>.
# Bug fix 2026-05-26 — было 2× dirname (см. [[Bug_AddUser_Sh_Snippet_Path_Fixed]]).
SCRIPT_DIR="$(cd "$(dirname "$(realpath "$0")")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
SNIPPET="$REPO_ROOT/caddy/users.caddy.snippet"
usage() {
echo "Usage: $0 <username> \"<comment>\"" >&2
exit 1
}
# ─── args ────────────────────────────────────────────────────────────────────
[[ $# -lt 2 ]] && usage
USERNAME="$1"
COMMENT="$2"
DATE="$(date -u +%Y-%m-%d)"
# Validate username: ^[a-z][a-z0-9_.-]{1,62}$
# Allows dot/underscore (john.doe, jane_smith) and 2-char names; starts with a
# letter. 63-char cap keeps it sane for a Caddyfile basic_auth entry (#427).
if ! [[ "$USERNAME" =~ ^[a-z][a-z0-9_.-]{1,62}$ ]]; then
echo "ERROR: invalid username '$USERNAME'. Must match ^[a-z][a-z0-9_.-]{1,62}$" >&2
exit 1
fi
# ─── idempotency guard ────────────────────────────────────────────────────────
# Escape regex metachars (notably the now-allowed '.') so the username is matched
# literally in the existence probe below (#427).
USERNAME_RE=$(printf '%s' "$USERNAME" | sed -E 's/[.[\^$*+?(){}|]/\\&/g')
if grep -qP "^\s+${USERNAME_RE}\s" "$SNIPPET" 2>/dev/null; then
echo "ERROR: user '$USERNAME' already exists in $SNIPPET. Remove first with remove_user.sh." >&2
exit 1
fi
# ─── password ─────────────────────────────────────────────────────────────────
if [ -t 0 ]; then
# Interactive — generate random password
PLAIN_PASS="$(LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c 16)"
else
# Piped input
read -r PLAIN_PASS
fi
if [[ -z "$PLAIN_PASS" ]]; then
echo "ERROR: empty password" >&2
exit 1
fi
# ─── bcrypt via caddy container ───────────────────────────────────────────────
# Caddy 2.11+ requires base64-encoded bcrypt in Caddyfile basic_auth.
# Password passed via stdin to avoid exposure in process list.
HASH="$(printf '%s' "$PLAIN_PASS" | docker run --rm -i caddy:2 sh -c '
pass=$(cat)
raw=$(caddy hash-password --algorithm bcrypt --plaintext "$pass")
printf "%s" "$raw" | base64 | tr -d "\n"
')"
if [[ -z "$HASH" ]]; then
echo "ERROR: caddy hash-password returned empty hash" >&2
exit 1
fi
# ─── insert into snippet ─────────────────────────────────────────────────────
# Insert before the closing brace of basic_auth block
if ! grep -q '^}' "$SNIPPET"; then
echo "ERROR: cannot find closing brace in $SNIPPET" >&2
exit 1
fi
# Use a temp file for safe in-place edit
TMP="$(mktemp)"
trap 'rm -f "$TMP"' EXIT
# Insert new user line before the last closing `}` of the basic_auth block
awk -v user="$USERNAME" -v hash="$HASH" -v comment="$COMMENT" -v date="$DATE" '
/^}/ && !done {
printf " %-7s %s # %s (%s)\n", user, hash, comment, date
done=1
}
{ print }
' "$SNIPPET" > "$TMP"
mv "$TMP" "$SNIPPET"
# ─── output ───────────────────────────────────────────────────────────────────
echo "Added user '$USERNAME' to $SNIPPET"
echo ""
echo "Plain password (save now — not stored anywhere):"
echo "$PLAIN_PASS"
echo ""
echo "Next steps:"
echo " 1. Commit caddy/users.caddy.snippet → PR → merge → GHA deploy"
echo " 2. After deploy, Caddy auto-reloads (deploy.yml does caddy reload)"
echo " 3. Send username + password to stakeholder via secure channel"