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:
@@ -0,0 +1,85 @@
|
||||
# Database Gateway — Cloudron Package
|
||||
|
||||
[Database Gateway](https://github.com/kazhuravlev/database-gateway) (dbgw) is
|
||||
a web gateway for secure, controlled access to PostgreSQL databases: users
|
||||
log in via OIDC and run SQL through a unified web UI, with every query
|
||||
parsed and authorized by embedded **OPA policies** (per user, target,
|
||||
operation and table). Results are stored in a local PostgreSQL with
|
||||
shareable links for debugging and auditing.
|
||||
|
||||
## Packaging overview
|
||||
|
||||
| Aspect | Choice |
|
||||
|--------|--------|
|
||||
| Pattern | Multi-stage Go build (JOURNAL pattern #2) |
|
||||
| Builder | `golang:1.26-alpine` (upstream's own build recipe, CGO on) |
|
||||
| Base image | `alpine:3.23` (musl runtime required — see below) |
|
||||
| Upstream | v0.24.0 (Go 1.26.1, Echo; frontend pre-built and embedded via `go:embed`) |
|
||||
| Addons | `localstorage` + `postgresql` (16) — storage for profiles, bookmarks, query results |
|
||||
| Auth | **Native OIDC (preferred)** wired to the Cloudron platform identity provider |
|
||||
| Runtime | `start.sh` waits for Postgres, seeds config + OPA policy, `migrate-up`, execs `run` |
|
||||
|
||||
Why alpine instead of `cloudron/base`: the SQL parser is a cgo binding
|
||||
(libpg_query) — the binary must be built with `CGO_ENABLED=1` (a static
|
||||
build fails with `undefined: pg.Parse`) and therefore links musl, which
|
||||
does not exec on Ubuntu/glibc bases. Alpine runtime matches upstream's own
|
||||
image; bash/psql/jq/openssl are apk-installed for `start.sh`.
|
||||
|
||||
Why not the official image: upstream publishes `ghcr.io/kazhuravlev/
|
||||
database-gateway:latest` but tagging is `:latest`-only; building v0.24.0
|
||||
from the cloned source pins the version and lets the ldflags carry the
|
||||
release tag into `/healthcheck`-style version output.
|
||||
|
||||
## Authentication (auth gate verdict: OIDC, preferred)
|
||||
|
||||
Database Gateway authenticates **only** via OIDC — no local user database,
|
||||
no LDAP. This is the preferred row of the auth matrix:
|
||||
|
||||
- `CLOUDRON_OIDC_ISSUER`, `CLOUDRON_OIDC_CLIENT_ID`,
|
||||
`CLOUDRON_OIDC_CLIENT_SECRET` (injected by Cloudron's platform OIDC
|
||||
provider, manifestVersion 2) are written into `/app/data/config.json` on
|
||||
first start.
|
||||
- Redirect URL: `${CLOUDRON_APP_ORIGIN}/auth/callback`.
|
||||
- Scopes: `openid profile email groups`; `role_claim` is `groups`, mapped
|
||||
`admins` → `admin`, `users` → `user` (Cloudron's built-in groups).
|
||||
- Access policy is then enforced per query by OPA (see below), not just at
|
||||
login.
|
||||
|
||||
## Ports
|
||||
|
||||
| Port | Type | Purpose |
|
||||
|------|------|---------|
|
||||
| 8080 | `httpPort` | Web UI + LRPC API (only listener) |
|
||||
|
||||
## Configuration (persistent, in `/app/data`)
|
||||
|
||||
- `config.json` — seeded once on first start (OIDC + storage from Cloudron
|
||||
env). **Add your database targets here** with the Cloudron file manager;
|
||||
restart the app after editing. See
|
||||
[upstream example](https://github.com/kazhuravlev/database-gateway/blob/master/example/config.json)
|
||||
for the full target format (connection, `default_schema`,
|
||||
`tables[].table` must be schema-qualified, `fields` allowlist).
|
||||
- `opa/simple.rego` — seeded default: Cloudron `admins` see all targets and
|
||||
may run any op; everyone else denied until an operator extends the policy
|
||||
(input subjects contain `user:<email>` and `role:<role>`).
|
||||
- `.cookie_secret` — random 32-byte hex, generated once; rotating it
|
||||
invalidates sessions.
|
||||
|
||||
The **postgresql addon** database is the gateway's own storage (users,
|
||||
bookmarks, cached results) — not a query target. Targets point at the
|
||||
*other* PostgreSQL instances you want to reach.
|
||||
|
||||
## Build & test
|
||||
|
||||
```bash
|
||||
docker build --cgroup-parent ukrrs-batch.slice -t database-gateway-cloudron:test Package-Workspace/Infrastructure/database-gateway/
|
||||
docker run --rm --entrypoint /usr/local/bin/database-gateway database-gateway-cloudron:test --version
|
||||
```
|
||||
|
||||
## Files
|
||||
|
||||
- `Dockerfile` — multi-stage Go build (golang:1.26-alpine → alpine:3.23, musl)
|
||||
- `CloudronManifest.json` — manifestVersion 2, httpPort 8080, localstorage + postgresql addons
|
||||
- `start.sh` — DB wait, config/policy seeding, migrations, exec gateway
|
||||
- `.env.example` — environment knobs consumed at seed time
|
||||
- `logo.png` — from upstream `frontend/src/favicon-96x96.png`
|
||||
Reference in New Issue
Block a user