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:
2026-09-01 19:28:06 -05:00
parent f918a90c3b
commit 3d38c507e8
10 changed files with 431 additions and 13 deletions
+74 -1
View File
@@ -4,7 +4,7 @@
**Project**: TSYSDevStack-SupportStack-Cloudron
**Goal**: Package ~57 applications for Cloudron PaaS platform
**Start Date**: 2025-01-24
**Current Status**: 11/~57 packages completed (~19%)
**Current Status**: 12/~57 packages completed (~21%)
## Completed Packages
@@ -893,6 +893,79 @@ traversal (frp/ngrok class, Rust); this package runs the **server** side
**Commit**: `feat: add Rathole Cloudron package (Infrastructure) [#650]`
### 12. Database Gateway (Infrastructure) ✅
**Date**: 2026-09-01
**Application**: Database Gateway (dbgw) — policy-checked web gateway to
PostgreSQL databases (OIDC login, OPA-authorized queries, cached results)
**Package Size**: 93.7MB (alpine runtime; smallest package so far)
**Port**: 8080 (httpPort, single listener — web UI + LRPC API)
**Addons**: localstorage, postgresql (16)
**Key Learnings**:
- **Auth gate verdict**: NATIVE OIDC, the preferred row — no local users,
no LDAP, the only login path is an OIDC provider. Perfect fit for the
Cloudron platform provider: `CLOUDRON_OIDC_ISSUER` / `CLIENT_ID` /
`CLIENT_SECRET` are written into `users.*` of the seeded config;
redirect URL derives from `CLOUDRON_APP_ORIGIN` + `/auth/callback`;
roles map from the `groups` claim (Cloudron `admins` → admin,
`users` → user). First package in the set to consume the platform
OIDC env vars end-to-end
- **cgo is load-bearing**: the SQL parser is a libpg_query cgo binding —
`CGO_ENABLED=0` fails to compile (`undefined: pg.Parse`). The binary
therefore links musl and will not exec on Ubuntu/glibc bases (fails
with a misleading "no such file or directory") → runtime base is
alpine:3.23, same as upstream's own image, with bash / jq / openssl /
postgresql16-client apk-installed for start.sh (the mirror image of the
Rathole glibc trap)
- **Config is a file, not env**: the app takes one JSON config (`-c`),
validated with required `role_mapping` and `policy.path`. start.sh
builds it with `jq -n --arg` on first run so secrets are escaped
safely into JSON, then admins edit `/app/data/config.json` (targets,
role mapping) and `/app/data/opa/simple.rego` (query policy) with the
Cloudron file manager; restart applies
- **Migrations**: goose SQL embedded in the binary; `migrate-up` subcommand
is run explicitly in start.sh (the app also self-migrates on `run`
belt and suspenders, both idempotent)
- **Frontend is pre-embedded**: upstream commits `internal/facade/ui/dist`
(`go:embed`), so the Go build ships the web UI — no Node stage needed
- **Validate() quirks**: targets list may be empty (seed `[]`), but every
`tables[].table` must be schema-qualified (`public.foo`) and every
role_mapping value must be exactly `admin` or `user`
**Build Process**:
- Upstream `kazhuravlev/database-gateway` v0.24.0 (GPL-3.0, Go 1.26.1)
built from the cloned repo (multi-stage pattern #2): golang:1.26-alpine
builder mirrors the upstream Dockerfile (CGO_ENABLED=1 + ldflags
version stamp) → alpine:3.23 runtime
- Logo: upstream `frontend/src/favicon-96x96.png` copied directly
- start.sh: psql wait loop on the postgresql addon → seed `.cookie_secret`
(openssl rand -hex 32), `opa/simple.rego` (admins-only default),
`config.json` (jq template) → `migrate-up``exec run`
**Validation**:
- `docker build --cgroup-parent ukrrs-batch.slice -t
database-gateway-cloudron:test` → green
- `--version` smoke in the image → `gateway version v0.24.0`
- End-to-end vs a throwaway postgres:16-alpine on a docker network with
fake CLOUDRON_* env: PG wait loop, config + policy seeding, all 5
goose migrations applied (bookmarks / query_results tables verified
with psql), rego policy compiled, gateway reached the OIDC discovery
step and failed only on the bogus issuer (expected off-Cloudron; the
real platform issuer resolves at install time)
**Files Created**:
- Dockerfile (multi-stage Go, CGO, alpine:3.23 runtime)
- CloudronManifest.json (manifestVersion 2, httpPort 8080, localstorage +
postgresql 16)
- start.sh (DB wait, jq config seed, policy seed, migrate-up, exec) —
committed executable
- README.md (auth story, config guide, target walkthrough)
- CHANGELOG.md
- .env.example (Cloudron-provided env documented)
- logo.png (96x96, upstream favicon)
**Commit**: `feat: add Database-Gateway Cloudron package (Infrastructure) [#639]`
---
## Packaging Pattern: Download Pre-Compiled Binaries