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).
85 lines
2.5 KiB
Go
85 lines
2.5 KiB
Go
// Package config loads api's configuration from environment variables,
|
|
// same convention as /ingest: no config file format for Phase 0.
|
|
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
type Config struct {
|
|
HTTPListenAddr string
|
|
ClickHouse ClickHouseConfig
|
|
Postgres PostgresConfig
|
|
SearchGRPCAddr string
|
|
QueryTimeout time.Duration
|
|
CORSAllowedOrigin string
|
|
EnterpriseAuthURL string
|
|
}
|
|
|
|
type ClickHouseConfig struct {
|
|
Addr string
|
|
Database string
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
// PostgresConfig is the control-plane metadata store (dashboards, panels
|
|
// -- see /docs/phase-3-dashboard-design.md), distinct from ClickHouse
|
|
// which remains log-data-only.
|
|
type PostgresConfig struct {
|
|
Addr string
|
|
Database string
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
func Load() (Config, error) {
|
|
cfg := Config{
|
|
HTTPListenAddr: getenv("HTTP_LISTEN_ADDR", ":8080"),
|
|
ClickHouse: ClickHouseConfig{
|
|
Addr: getenv("CLICKHOUSE_ADDR", "localhost:9000"),
|
|
Database: getenv("CLICKHOUSE_DATABASE", "sentry"),
|
|
Username: getenv("CLICKHOUSE_USERNAME", "default"),
|
|
Password: getenv("CLICKHOUSE_PASSWORD", ""),
|
|
},
|
|
Postgres: PostgresConfig{
|
|
Addr: getenv("POSTGRES_ADDR", "localhost:5432"),
|
|
Database: getenv("POSTGRES_DATABASE", "sentry_metadata"),
|
|
Username: getenv("POSTGRES_USERNAME", "sentry"),
|
|
Password: getenv("POSTGRES_PASSWORD", ""),
|
|
},
|
|
// Search service's gRPC address (see /search) -- default matches
|
|
// /search's own default GRPC_LISTEN_ADDR.
|
|
SearchGRPCAddr: getenv("SEARCH_GRPC_ADDR", "localhost:50052"),
|
|
// Phase 0 has no auth, so this is wide open by default to keep
|
|
// the local SvelteKit dev server (a different origin/port)
|
|
// working out of the box. Tighten before this is ever reachable
|
|
// from outside a trusted dev/homelab network.
|
|
CORSAllowedOrigin: getenv("CORS_ALLOWED_ORIGIN", "*"),
|
|
// Empty by default -- a single-tenant deployment without
|
|
// enterprise/ configured runs with authz.RequireRole* as a
|
|
// no-op, matching Phase 0-3 behavior. Set to enterprise-auth's
|
|
// base URL (e.g. "http://enterprise-auth:8081") to turn on
|
|
// real session/service-token enforcement.
|
|
EnterpriseAuthURL: getenv("ENTERPRISE_AUTH_URL", ""),
|
|
}
|
|
|
|
timeoutSec, err := strconv.Atoi(getenv("QUERY_TIMEOUT_SECONDS", "30"))
|
|
if err != nil {
|
|
return Config{}, fmt.Errorf("QUERY_TIMEOUT_SECONDS: %w", err)
|
|
}
|
|
cfg.QueryTimeout = time.Duration(timeoutSec) * time.Second
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func getenv(key, fallback string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return fallback
|
|
}
|