Full rebrand across cosmetic branding, code identifiers, and infrastructure/data-plane naming, using the supplied Cairn OBS logo package. Cosmetic: favicon/logo swap (also closes a stale license-audit finding -- the old favicon was SvelteKit's unreplaced scaffold logo), new centered welcome landing page, larger/legible sidebar logo, page titles, CLAUDE.md/README/docs prose. Code identifiers: Go module path github.com/sentry/sentry -> github.com/cairnobs/cairnobs across all 13 modules and ~91 files (protoc regenerated); Rust crates sentry-agent/sentry-parser/sentry-search -> cairnobs-*; CLI sentryctl -> cairnobsctl; Terraform provider fully renamed (sentry_dashboard etc. -> cairnobs_dashboard, provider type, env vars); every session/auth cookie name; agent config paths and Windows service identity. Deliberately preserved: the gRPC wire protocol's protobuf packages (sentry.logs.v1, sentry.agent.v1) and their Go import directory (proto/sentry/...) -- renaming the wire-level package would break every currently-deployed agent binary (confirmed two real hosts, including mail.inbuxa.com, are actively streaming through this exact contract) until rebuilt and redeployed in lockstep with an ingest cutover. Only the Go module path wrapping the generated code changes. Infrastructure: every docker-compose container name (root and three component-level compose files); the Helm chart (directory, Chart.yaml, named-template helpers, all templates, values.yaml image repos); Kubernetes Operator (CRD group sentry.io -> cairnobs.io, both CRD YAML files, Go identifiers, RBAC markers); the coupled enterprise/tenantcrd package. Caught and fixed real path-coupling bugs along the way: the Helm chart's search/ingest volume mounts and the dev-only-credential detection constant vs. docker-compose.yml's literal values had to move together or a security warning would have silently stopped firing. Data plane: Postgres database sentry_metadata -> cairnobs_metadata and role sentry -> cairnobs; ClickHouse database sentry -> cairnobs; Kafka topic sentry.logs.raw -> cairnobs.logs.raw and its consumer groups. Source-level defaults, docker-compose.yml, and every migrate.sh/ provision script default updated together; already-applied migration files left untouched per this repo's immutable-migration convention. Verified at every layer: all 13 Go modules build/vet/test clean, both Rust workspaces (agent, search) build/clippy/test clean, npm run check/ build clean, docker compose config validates on all four compose files. Live-verified against a real docker stack multiple times through this work, including a final fresh-volume run confirming the actual renamed Postgres database/role, ClickHouse database, and Kafka topic all work end to end with a real login and query, zero console errors.
152 lines
6.2 KiB
Go
152 lines
6.2 KiB
Go
// Package config loads enterprise-auth's configuration from environment
|
|
// variables, same convention as every other Go service in this repo.
|
|
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
type Config struct {
|
|
HTTPListenAddr string
|
|
Postgres PostgresConfig
|
|
OIDC OIDCConfig
|
|
SAML SAMLConfig
|
|
SessionSigningKey []byte
|
|
// PostLoginRedirectURL is where the browser lands after
|
|
// internal/loginhandler sets a session cookie -- web's base URL in
|
|
// a real deployment.
|
|
PostLoginRedirectURL string
|
|
// SelectTenantRedirectURL is where the browser lands after a login
|
|
// resolves to more than one tenant_memberships row --
|
|
// internal/loginhandler issues a pending-login cookie and sends the
|
|
// browser here. web/src/routes/select-tenant is the page that serves
|
|
// it (see that route's own comments) -- it calls GET
|
|
// /auth/memberships and POST /auth/select-tenant with
|
|
// `credentials: 'include'`, which is why CORSAllowedOrigin below has
|
|
// to be a literal origin, not WithCORS's zero-config "*" default.
|
|
SelectTenantRedirectURL string
|
|
// CORSAllowedOrigin is passed to httpserver.WithCredentialedCORS, not
|
|
// the plain httpserver.WithCORS every other service in this repo
|
|
// uses -- GET /auth/memberships / POST /auth/select-tenant are
|
|
// cookie-carrying requests (the pending-login cookie, then the real
|
|
// session cookie), and browsers categorically refuse to combine a
|
|
// credentialed fetch with an Access-Control-Allow-Origin: "*"
|
|
// response, so this can't default to the wildcard the way
|
|
// api/internal/config.CORSAllowedOrigin does.
|
|
CORSAllowedOrigin string
|
|
}
|
|
|
|
// devOnlyCredential is docker-compose.yml's zero-config default for
|
|
// every Postgres/ClickHouse password in this repo -- see
|
|
// api/internal/config.Config.DevCredentialWarnings for the full
|
|
// reasoning (duplicated here per this repo's no-shared-code-between-
|
|
// services convention). enterprise-auth also has its own dev-only
|
|
// literal for ENTERPRISE_SESSION_SIGNING_KEY, checked alongside it below
|
|
// -- per the threat model doc, this is "the single highest-value secret
|
|
// in the enterprise deployment," since compromising it lets an attacker
|
|
// forge any identity, including the RoleService credential.
|
|
const (
|
|
devOnlyCredential = "cairnobs-dev-only"
|
|
devOnlySigningKey = "cairnobs-dev-only-session-signing-key-32bytes+"
|
|
)
|
|
|
|
// DevCredentialWarnings reports which configured secrets still equal
|
|
// their literal dev-only defaults -- cmd/enterprise-*/main.go logs each
|
|
// one loudly at startup. A warning, not a startup-refusing error: local
|
|
// dev's zero-config docker-compose.yml path legitimately leaves these
|
|
// at their default values.
|
|
func (c Config) DevCredentialWarnings() []string {
|
|
var warnings []string
|
|
if c.Postgres.Password == devOnlyCredential {
|
|
warnings = append(warnings, "POSTGRES_PASSWORD is still the default dev-only value -- set a real password before this is reachable outside local dev")
|
|
}
|
|
if string(c.SessionSigningKey) == devOnlySigningKey {
|
|
warnings = append(warnings, "ENTERPRISE_SESSION_SIGNING_KEY is still the default dev-only value -- this is the single highest-value secret in an enterprise deployment (compromise lets an attacker forge any identity); set a real, random one before this is reachable outside local dev")
|
|
}
|
|
return warnings
|
|
}
|
|
|
|
type PostgresConfig struct {
|
|
Addr string
|
|
Database string
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
// OIDCConfig is optional -- a deployment might configure OIDC, SAML,
|
|
// both, or (during early rollout) neither yet. Load() doesn't fail if
|
|
// these are unset; internal/oidc.New is only called once IssuerURL is
|
|
// actually present.
|
|
type OIDCConfig struct {
|
|
IssuerURL string
|
|
ClientID string
|
|
ClientSecret string
|
|
RedirectURL string
|
|
}
|
|
|
|
// SAMLConfig is likewise optional. cmd/enterprise-auth/main.go fetches
|
|
// and parses IDPMetadataURL into the *saml.EntityDescriptor
|
|
// internal/saml.New requires at startup (crewjam/saml's
|
|
// samlsp.FetchMetadata) -- this struct just carries the raw config
|
|
// values this package's job (env-var loading) is scoped to.
|
|
type SAMLConfig struct {
|
|
EntityID string
|
|
ACSURL string
|
|
IDPMetadataURL string
|
|
}
|
|
|
|
func Load() (Config, error) {
|
|
cfg := Config{
|
|
HTTPListenAddr: getenv("HTTP_LISTEN_ADDR", ":8082"),
|
|
Postgres: PostgresConfig{
|
|
Addr: getenv("POSTGRES_ADDR", "localhost:5432"),
|
|
Database: getenv("POSTGRES_DATABASE", "sentry_metadata"),
|
|
Username: getenv("POSTGRES_USERNAME", "sentry"),
|
|
Password: getenv("POSTGRES_PASSWORD", ""),
|
|
},
|
|
OIDC: OIDCConfig{
|
|
IssuerURL: getenv("OIDC_ISSUER_URL", ""),
|
|
ClientID: getenv("OIDC_CLIENT_ID", ""),
|
|
ClientSecret: getenv("OIDC_CLIENT_SECRET", ""),
|
|
RedirectURL: getenv("OIDC_REDIRECT_URL", ""),
|
|
},
|
|
SAML: SAMLConfig{
|
|
EntityID: getenv("SAML_ENTITY_ID", ""),
|
|
ACSURL: getenv("SAML_ACS_URL", ""),
|
|
IDPMetadataURL: getenv("SAML_IDP_METADATA_URL", ""),
|
|
},
|
|
PostLoginRedirectURL: getenv("POST_LOGIN_REDIRECT_URL", "http://localhost:3000"),
|
|
}
|
|
// Defaults relative to PostLoginRedirectURL if not set explicitly --
|
|
// computed after cfg.PostLoginRedirectURL above so a caller
|
|
// overriding just POST_LOGIN_REDIRECT_URL still gets a sensible
|
|
// SelectTenantRedirectURL without also having to set the new
|
|
// variable. CORSAllowedOrigin defaults the same way: web's own
|
|
// origin is exactly what needs credentialed cross-origin access to
|
|
// this service.
|
|
cfg.SelectTenantRedirectURL = getenv("SELECT_TENANT_REDIRECT_URL", cfg.PostLoginRedirectURL+"/select-tenant")
|
|
cfg.CORSAllowedOrigin = getenv("CORS_ALLOWED_ORIGIN", cfg.PostLoginRedirectURL)
|
|
|
|
// Required, unlike OIDC/SAML above: every enterprise-auth deployment
|
|
// issues and validates session/service tokens (internal/session),
|
|
// even one that hasn't configured any IdP yet. 32 bytes matches
|
|
// internal/session.MinSigningKeyBytes -- not imported here to avoid
|
|
// a config->session dependency for one constant, but the two values
|
|
// must be kept in sync.
|
|
signingKey := getenv("ENTERPRISE_SESSION_SIGNING_KEY", "")
|
|
if len(signingKey) < 32 {
|
|
return Config{}, fmt.Errorf("ENTERPRISE_SESSION_SIGNING_KEY must be set to at least 32 bytes (got %d)", len(signingKey))
|
|
}
|
|
cfg.SessionSigningKey = []byte(signingKey)
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func getenv(key, fallback string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return fallback
|
|
}
|