officer-setup: the environment section
Writes .env, and the whole point of the section is the two values it must not write twice. JWT_SECRET and VAULT_STORE_KEY are read back from any existing .env and kept. The original script reminted JWT_SECRET on every run that agreed to regenerate .env, which logs every device out with no stated reason, and never wrote VAULT_STORE_KEY at all — so a scripted install had no at-rest key and the vault and wallet refused to store anything. VAULT_STORE_KEY is the more dangerous of the two now that it is being written. It is not Vaultwarden's despite the name: it encrypts every secret column in Postgres, and the wallet seed envelope on top of the owner passphrase. Changing it is unrecoverable for the seed, because the passphrase opens the inner envelope and that is the outer one. Said in the section, in the file it writes, and in .env.example, which described it as Vaultwarden's and understated it. DATA_PATH and OFFICER_ITEMS_DIR are derived from $OFFICER_ROOT rather than asked — two questions that had to agree with each other and with the app store. ALLOW_ANY_ORIGIN is written explicitly from whether tailscale0 exists, rather than left to the platform default. The default is ON, which CLAUDE.md says is only defensible because the tailnet is the perimeter; with no tailnet there is no perimeter, so it goes out as false. Added to .env.example, which omitted it. PORT defaults to 9000, matching .env.example. The old script used 9010; nothing depends on either, and it is a prompt. write_env restores the prior umask. It was set to 077 so the secrets are never briefly world-readable, but umask is not scoped to a function and would have made every file the later sections create owner-only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -46,6 +46,8 @@ source "$SCRIPT_DIR/officer-setup/lib/repo.sh"
|
||||
source "$SCRIPT_DIR/officer-setup/lib/layout.sh"
|
||||
# shellcheck source=officer-setup/lib/postgres.sh
|
||||
source "$SCRIPT_DIR/officer-setup/lib/postgres.sh"
|
||||
# shellcheck source=officer-setup/lib/env.sh
|
||||
source "$SCRIPT_DIR/officer-setup/lib/env.sh"
|
||||
|
||||
trap 'echo ""; echo -e "${RED}╔══════════════════════════════════════════════════╗${NC}"; echo -e "${RED}║ OFFICER SETUP FAILED${NC}"; echo -e "${RED}║ Step: ${CURRENT_STEP:-unknown}${NC}"; echo -e "${RED}║ Line: $LINENO${NC}"; echo -e "${RED}║ Command: $BASH_COMMAND${NC}"; echo -e "${RED}╚══════════════════════════════════════════════════╝${NC}"' ERR
|
||||
|
||||
@@ -447,10 +449,111 @@ if ! skip; then
|
||||
step_ok
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# 6. Environment
|
||||
# =============================================================================
|
||||
|
||||
step "Environment"
|
||||
if ! skip; then
|
||||
echo ""
|
||||
info "Environment — $(env_file)"
|
||||
|
||||
# Read back before anything is asked. The two secrets below are kept, never
|
||||
# reminted, and everything else becomes the default for its question.
|
||||
ENV_JWT_SECRET="$(env_get JWT_SECRET)"
|
||||
ENV_VAULT_STORE_KEY="$(env_get VAULT_STORE_KEY)"
|
||||
ENV_PORT="$(env_get PORT)"
|
||||
ENV_PUBLIC_URL="$(env_get PUBLIC_URL)"
|
||||
ENV_MAIL_TRANSPORT="$(env_get MAIL_TRANSPORT)"
|
||||
ENV_DISCORD_WEBHOOK="$(env_get DISCORD_BUG_REPORT_WEBHOOK)"
|
||||
ENV_BROWSER_RELAY_PORT="$(env_get BROWSER_RELAY_PORT)"
|
||||
|
||||
if env_exists; then
|
||||
echo " exists — its values are the defaults below, and the two secrets are kept"
|
||||
else
|
||||
echo " does not exist yet"
|
||||
fi
|
||||
|
||||
# ── the secrets ──
|
||||
if [[ -n "$ENV_JWT_SECRET" ]]; then
|
||||
echo " JWT_SECRET: kept (regenerating it logs everybody out)"
|
||||
else
|
||||
ENV_JWT_SECRET="$(generate_secret)"
|
||||
echo " JWT_SECRET: generated"
|
||||
fi
|
||||
|
||||
if [[ -n "$ENV_VAULT_STORE_KEY" ]]; then
|
||||
echo " VAULT_STORE_KEY: kept"
|
||||
else
|
||||
ENV_VAULT_STORE_KEY="$(generate_secret)"
|
||||
echo " VAULT_STORE_KEY: generated"
|
||||
echo ""
|
||||
warn "back up VAULT_STORE_KEY somewhere safe, now."
|
||||
echo " It encrypts every upstream credential the platform stores, and the"
|
||||
echo " wallet's seed on top of your passphrase. Lose it and those are gone"
|
||||
echo " — the passphrase does not help, because it opens the inner envelope"
|
||||
echo " and this is the outer one."
|
||||
fi
|
||||
|
||||
# ── what is asked ──
|
||||
echo ""
|
||||
ask_required ENV_PORT "Port Officer listens on" "${ENV_PORT:-9000}"
|
||||
ENV_BROWSER_RELAY_PORT="${ENV_BROWSER_RELAY_PORT:-18792}"
|
||||
|
||||
echo ""
|
||||
echo " PUBLIC_URL is where Officer is reached from a browser. Allowed"
|
||||
echo " origins are derived from it, and passkeys are bound to its host —"
|
||||
echo " so it has to be the address you actually use, not localhost, unless"
|
||||
echo " localhost is genuinely it."
|
||||
ask_required ENV_PUBLIC_URL "Public URL" "${ENV_PUBLIC_URL:-http://localhost:${ENV_PORT}}"
|
||||
|
||||
# ── origin checking, decided by the machine rather than by a default ──
|
||||
#
|
||||
# ALLOW_ANY_ORIGIN defaults to ON inside the platform, which CLAUDE.md says is
|
||||
# only defensible because the tailnet is the perimeter. So the value is written
|
||||
# explicitly here, from whether this machine actually has one.
|
||||
if tailnet_present; then
|
||||
ENV_ALLOW_ANY_ORIGIN="true"
|
||||
ORIGIN_WHY="tailscale0 is up, so the tailnet is the perimeter"
|
||||
else
|
||||
ENV_ALLOW_ANY_ORIGIN="false"
|
||||
ORIGIN_WHY="no tailnet on this machine, so origin checking is left ON"
|
||||
fi
|
||||
echo ""
|
||||
echo " ALLOW_ANY_ORIGIN=${ENV_ALLOW_ANY_ORIGIN} — ${ORIGIN_WHY}"
|
||||
|
||||
echo ""
|
||||
ENV_MAIL_TRANSPORT="${ENV_MAIL_TRANSPORT:-}"
|
||||
read -rp " Mail transport, blank for none [${ENV_MAIL_TRANSPORT}]: " REPLY_MAIL || true
|
||||
ENV_MAIL_TRANSPORT="${REPLY_MAIL:-$ENV_MAIL_TRANSPORT}"
|
||||
|
||||
echo ""
|
||||
echo " to write:"
|
||||
echo " PORT=${ENV_PORT} BROWSER_RELAY_PORT=${ENV_BROWSER_RELAY_PORT}"
|
||||
echo " PUBLIC_URL=${ENV_PUBLIC_URL}"
|
||||
echo " ALLOW_ANY_ORIGIN=${ENV_ALLOW_ANY_ORIGIN}"
|
||||
echo " DATA_PATH=${OFFICER_ROOT}/data"
|
||||
echo " OFFICER_ITEMS_DIR=${OFFICER_ROOT}/capabilities"
|
||||
echo " HOME_DIR=${USER_HOME}"
|
||||
echo " POSTGRES_URL=${POSTGRES_URL%%:*}://…"
|
||||
echo " JWT_SECRET, VAULT_STORE_KEY — not shown"
|
||||
echo ""
|
||||
|
||||
if confirm "Write it?"; then
|
||||
write_env
|
||||
ok "written, 0600, owned by ${USERNAME}"
|
||||
[[ -f "$(env_file).before-officer-setup" ]] && echo " previous kept as $(env_file).before-officer-setup"
|
||||
SUMMARY+=("Environment: $(env_file)")
|
||||
else
|
||||
warn "skipped by request"
|
||||
SUMMARY+=("Environment: SKIPPED by request")
|
||||
fi
|
||||
step_ok
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# NOT BUILT YET
|
||||
# =============================================================================
|
||||
# 5 Environment .env
|
||||
# 6 Schema db:push
|
||||
# 7 Build gen:index
|
||||
# 8 Services pm2 startOrRestart · save · startup
|
||||
|
||||
Reference in New Issue
Block a user