Phase 4: SSO scaffolding, RBAC enforcement, tenant-scoped dashboards, audit logging, K8s deployment
RBAC (api/internal/authz) is live on /query and /dashboards, backed by a new enterprise/ module (session issuance, audit logging, RBAC storage, OIDC/SAML protocol wiring) that core never imports -- only calls over HTTP. Found and fixed a real cross-tenant vulnerability in dashboards (no tenant_id filtering at all) while writing the threat model doc. Two things are explicitly NOT done, documented rather than hidden: tenant isolation for log data itself (/query still shares one ClickHouse connection and Tantivy index across every tenant -- RBAC controls who can query, not what a query can see), and human SSO login (protocol wiring exists, no HTTP handler calls it yet). See docs/security/threat-model.md and docs/phase-4-runbook.md. Also adds deploy/ (Go Operator + Helm chart, validated offline only -- no cluster was reachable in this environment).
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
# deploy
|
||||
|
||||
Kubernetes deployment for Sentry, added in Phase 4 (`/deploy` was
|
||||
deliberately stubbed through Phase 3 -- see `/CLAUDE.md`'s Phase 3
|
||||
non-goals). Two pieces:
|
||||
|
||||
- `operator/` -- a small Go controller-runtime Operator managing one CRD
|
||||
(`Tenant`). See `operator/README.md`.
|
||||
- `helm/sentry/` -- a Helm chart covering every `docker-compose.yml`
|
||||
service, plus the operator and `Tenant` CRs when
|
||||
`enterprise.enabled=true`. See `helm/sentry/README.md`.
|
||||
|
||||
## What "multi-tenant-aware" means here, precisely
|
||||
|
||||
Per `/docs/phase-4-isolation-design.md`, tenant isolation itself lives at
|
||||
the **application layer** inside `enterprise/` (one `api` process holds a
|
||||
map of per-tenant ClickHouse connection pools; one `search` process holds
|
||||
a map of per-tenant Tantivy indices) -- not at the Kubernetes layer. This
|
||||
directory is **not** "one Deployment per tenant" or a general
|
||||
multi-cluster system; that's an explicit Phase 4 non-goal (see
|
||||
`/CLAUDE.md`). What it *does* add, matching that same document's exit
|
||||
criteria ("real per-tenant secret management, replacing today's single
|
||||
shared `CLICKHOUSE_PASSWORD`"):
|
||||
|
||||
- A `Tenant` CRD + controller that generates and manages one dedicated
|
||||
ClickHouse credential Secret per tenant (`operator/internal/controller`).
|
||||
- A Helm chart that can install zero-or-more `Tenant` CRs
|
||||
(`values.tenants`) alongside the rest of the stack.
|
||||
|
||||
The Operator does **not** call ClickHouse (no `CREATE DATABASE`/`CREATE
|
||||
USER`/`GRANT`) and does not touch the Tantivy filesystem or
|
||||
`enterprise/internal/rbacstore` -- that's `enterprise/internal/
|
||||
tenantprovision`, still unbuilt (see the Phase 4 task 5 summary). A
|
||||
`Tenant` reaching `status.phase: Active` here means "this tenant has a
|
||||
K8s Secret," not "this tenant's ClickHouse database/grants exist" --
|
||||
those are two different systems' state machines that aren't reconciled
|
||||
together yet, named explicitly rather than implied.
|
||||
|
||||
## Verification status -- read before trusting this against a real cluster
|
||||
|
||||
**Not verified against a live Kubernetes cluster.** This environment has
|
||||
no `kubectl`/`kind`/`minikube`/`kubebuilder`/cluster reachable, so
|
||||
nothing here has been `kubectl apply`'d or `helm install`'d for real.
|
||||
Same disclosed-limitation shape as `/agent/README.md`'s "Windows-specific
|
||||
agent code remains unverified on real Windows" from Phase 1 -- a real gap
|
||||
to close before shipping, not swept under the rug.
|
||||
|
||||
What **was** actually verified, offline, in this environment (network
|
||||
access was available to fetch these tools, but no cluster):
|
||||
|
||||
- `deploy/operator`: `go build`/`go vet`/`go test ./...` all pass,
|
||||
including reconciler tests against controller-runtime's fake client
|
||||
(`internal/controller/tenant_controller_test.go`) -- real reconcile
|
||||
logic exercised, but not against a real apiserver (no `envtest`
|
||||
binaries available; see that test file's doc comment).
|
||||
- `deploy/operator/config/crd/sentry.io_tenants.yaml`: parsed with
|
||||
`sigs.k8s.io/yaml` + strict-unmarshaled into the real
|
||||
`k8s.io/apiextensions-apiserver` `CustomResourceDefinition` Go type --
|
||||
catches YAML syntax errors and structural mistakes, not a live-cluster
|
||||
admission check.
|
||||
- `deploy/helm/sentry`: `helm lint` passes; `helm template` renders
|
||||
cleanly under both default values and a `enterprise.enabled: true` +
|
||||
two-tenant override; the rendered output was checked with `kubeconform
|
||||
-strict` against the real Kubernetes 1.31 OpenAPI schema for every
|
||||
built-in resource kind (22-29 resources depending on values, 0
|
||||
invalid) -- this catches schema mistakes (wrong field names, wrong
|
||||
types) but not whether the resources actually reconcile correctly
|
||||
together on a live cluster (Job/StatefulSet startup ordering, PVC
|
||||
provisioning, actual pod scheduling).
|
||||
- Docker image builds (`operator/Dockerfile` and every other
|
||||
`Dockerfile` this chart references) were **not** verified in this
|
||||
session -- Docker's daemon wasn't reachable here either (see the
|
||||
Phase 4 task 5 conversation for why). Build and push every image this
|
||||
chart's `values.yaml` references before installing it.
|
||||
|
||||
Before relying on this in production: `kind create cluster`, `helm
|
||||
install` with `--include-crds`, and walk through
|
||||
`helm/sentry/README.md`'s two-tenant example end to end.
|
||||
Reference in New Issue
Block a user