feat: add Database-Gateway Cloudron package (Infrastructure) [#639]
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
This commit is contained in:
+120
@@ -0,0 +1,120 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user