Database Gateway 0.24.0 as the 12th package: multi-stage Go build (CGO required by the libpg_query parser, hence an alpine:3.23 runtime matching upstream), native OIDC wired to the Cloudron platform identity provider, postgresql addon storage with goose migrations applied at start, jq-generated config + OPA policy seeded to /app/data. Verified end-to-end against a throwaway postgres (migrations, policy compile, startup to the OIDC handoff). Docs gardened to 12 packages. Ticket: https://projects.knownelement.com/issues/639
121 lines
4.0 KiB
Bash
Executable File
121 lines
4.0 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
# Database Gateway runtime setup:
|
|
# 1. wait for the Cloudron postgresql addon (storage for profiles/bookmarks/
|
|
# query results)
|
|
# 2. seed /app/data/config.json + /app/data/opa/ on first run
|
|
# 3. apply the embedded schema migrations (migrate-up)
|
|
# 4. exec the gateway (web UI + LRPC API on 0.0.0.0:8080)
|
|
|
|
CONFIG_PATH="/app/data/config.json"
|
|
OPA_DIR="/app/data/opa"
|
|
COOKIE_SECRET_PATH="/app/data/.cookie_secret"
|
|
|
|
# --- 1. wait for PostgreSQL -------------------------------------------------
|
|
DB_HOST=${CLOUDRON_POSTGRESQL_HOST:-127.0.0.1}
|
|
DB_PORT=${CLOUDRON_POSTGRESQL_PORT:-5432}
|
|
DB_NAME=${CLOUDRON_POSTGRESQL_DATABASE:-dbgw}
|
|
DB_USER=${CLOUDRON_POSTGRESQL_USERNAME:-dbgw}
|
|
DB_PASSWORD=${CLOUDRON_POSTGRESQL_PASSWORD}
|
|
|
|
echo "Waiting for PostgreSQL at ${DB_HOST}:${DB_PORT} ..."
|
|
until PGPASSWORD="${DB_PASSWORD}" psql -h "${DB_HOST}" -p "${DB_PORT}" -U "${DB_USER}" -d "${DB_NAME}" -c '\q' >/dev/null 2>&1; do
|
|
echo "PostgreSQL is unavailable - sleeping"
|
|
sleep 2
|
|
done
|
|
echo "PostgreSQL is up"
|
|
|
|
# --- 2. seed persistent configuration ---------------------------------------
|
|
# Persistent cookie secret (rotating it would invalidate live sessions).
|
|
if [[ ! -f "${COOKIE_SECRET_PATH}" ]]; then
|
|
umask 077
|
|
openssl rand -hex 32 > "${COOKIE_SECRET_PATH}"
|
|
echo "Generated new cookie secret"
|
|
fi
|
|
COOKIE_SECRET=$(cat "${COOKIE_SECRET_PATH}")
|
|
|
|
# Default OPA policy: Cloudron "admins" see every target and may run any op;
|
|
# everyone else is denied until an operator extends the policy. Edit with the
|
|
# Cloudron file manager; changes apply on restart.
|
|
mkdir -p "${OPA_DIR}"
|
|
if [[ ! -f "${OPA_DIR}/simple.rego" ]]; then
|
|
cat > "${OPA_DIR}/simple.rego" <<'EOF'
|
|
package gateway
|
|
|
|
# Default Cloudron policy: the admins group is unrestricted; everyone else
|
|
# is denied until an operator edits this file (input.subjects contains
|
|
# user:<email> and role:<role> principals).
|
|
default allow_target := false
|
|
default allow_query := false
|
|
|
|
allow_target if {
|
|
"role:admin" in input.subjects
|
|
}
|
|
|
|
allow_query if {
|
|
"role:admin" in input.subjects
|
|
}
|
|
EOF
|
|
echo "Seeded default OPA policy at ${OPA_DIR}/simple.rego"
|
|
fi
|
|
|
|
# Seed the app config once (jq handles escaping of secrets into JSON). OIDC
|
|
# comes from the Cloudron platform identity provider; targets (the databases
|
|
# this gateway may reach) are added by the operator by editing
|
|
# ${CONFIG_PATH} with the Cloudron file manager.
|
|
if [[ ! -f "${CONFIG_PATH}" ]]; then
|
|
jq -n \
|
|
--arg db_host "${DB_HOST}" \
|
|
--arg db_name "${DB_NAME}" \
|
|
--arg db_user "${DB_USER}" \
|
|
--arg db_password "${DB_PASSWORD}" \
|
|
--argjson db_port "${DB_PORT}" \
|
|
--arg issuer "${CLOUDRON_OIDC_ISSUER}" \
|
|
--arg client_id "${CLOUDRON_OIDC_CLIENT_ID}" \
|
|
--arg client_secret "${CLOUDRON_OIDC_CLIENT_SECRET}" \
|
|
--arg origin "${CLOUDRON_APP_ORIGIN}" \
|
|
--arg cookie_secret "${COOKIE_SECRET}" \
|
|
--arg opa_dir "${OPA_DIR}" \
|
|
'{
|
|
targets: [],
|
|
users: {
|
|
client_id: $client_id,
|
|
client_secret: $client_secret,
|
|
issuer_url: $issuer,
|
|
redirect_url: ($origin + "/auth/callback"),
|
|
scopes: ["openid", "profile", "email", "groups"],
|
|
role_claim: "groups",
|
|
role_mapping: {
|
|
admins: "admin",
|
|
users: "user"
|
|
}
|
|
},
|
|
policy: { path: $opa_dir },
|
|
facade: {
|
|
port: 8080,
|
|
cookie_secret: $cookie_secret,
|
|
unsafe_cors_allow_all: false
|
|
},
|
|
storage: {
|
|
host: $db_host,
|
|
port: $db_port,
|
|
database: $db_name,
|
|
username: $db_user,
|
|
password: $db_password,
|
|
use_ssl: false,
|
|
max_pool_size: 16
|
|
}
|
|
}' > "${CONFIG_PATH}"
|
|
chmod 600 "${CONFIG_PATH}"
|
|
echo "Seeded config at ${CONFIG_PATH}"
|
|
fi
|
|
|
|
# --- 3. migrations -----------------------------------------------------------
|
|
echo "Applying database migrations ..."
|
|
/usr/local/bin/database-gateway -c "${CONFIG_PATH}" migrate-up
|
|
|
|
# --- 4. run --------------------------------------------------------------------
|
|
echo "Starting Database Gateway on :8080 ..."
|
|
exec /usr/local/bin/database-gateway -c "${CONFIG_PATH}" run
|