Documentation
¶
Overview ¶
Package dbaccess manages database users, temporary credentials and network scope. Statements use quoted identifiers and a pre-hashed password verifier, so no plaintext secret reaches SQL text, container logs or an audit row.
Index ¶
- Constants
- Variables
- func Allows(scope Scope, db, app Placement) (bool, string)
- func CreateRoleSQL(p CreateParams) (string, error)
- func DropRoleSQL(role, adminRole string) (string, error)
- func GeneratePassword() (string, error)
- func NewTempRoleName() (string, error)
- func Protected(r Role, adminRole string) bool
- func QuoteIdent(id string) string
- func QuoteLiteral(s string) string
- func ReadConnections(ctx context.Context, ex Exec, containerID, engine string) (used, limit int, err error)
- func ReadDiskUsedPercent(ctx context.Context, ex Exec, containerID, engine string) (float64, error)
- func RotateSQL(role, verifier string) (string, error)
- func ScramVerifier(password string) (string, error)
- func SetLoginSQL(role string, login bool) (string, error)
- func SweepIntervalFromEnv(getenv func(string) string) time.Duration
- func TempPresetAllowed(p Preset) bool
- func ValidPreset(p Preset) bool
- func ValidateRoleName(name, adminRole string) error
- type CreateParams
- type CreateParamsIn
- type Credential
- type Exec
- type Placement
- type Postgres
- func (p Postgres) Create(ctx context.Context, in CreateParamsIn) (Credential, error)
- func (p Postgres) Drop(ctx context.Context, name string) error
- func (p Postgres) DropTemp(ctx context.Context, name string) error
- func (p Postgres) ListRoles(ctx context.Context) ([]Role, error)
- func (p Postgres) Rotate(ctx context.Context, name string) (Credential, error)
- func (p Postgres) SetLogin(ctx context.Context, name string, login bool) error
- func (p Postgres) SetOwnerPassword(ctx context.Context, password string) error
- func (p Postgres) SetRequireTLS(ctx context.Context, require bool) error
- func (p Postgres) TLSState(ctx context.Context) (string, error)
- type Preset
- type Revoker
- type Role
- type Scope
- type StatementError
- type StoreAdapter
- func (a StoreAdapter) ClaimTempCredential(ctx context.Context, id string, now time.Time, lease time.Duration) (bool, error)
- func (a StoreAdapter) CompleteTempCredential(ctx context.Context, id string, now time.Time) error
- func (a StoreAdapter) DueTempCredentials(ctx context.Context, now time.Time) ([]TempCredential, error)
- func (a StoreAdapter) ReleaseTempCredential(ctx context.Context, id string) error
- type SweepAuditor
- type SweepStore
- type Sweeper
- type TTLLimits
- type TempCredential
- type Verdict
Constants ¶
const ( ActionUserCreate = "database.user.create" ActionUserRotate = "database.user.rotate" ActionUserDisable = "database.user.disable" ActionUserEnable = "database.user.enable" ActionUserDelete = "database.user.delete" ActionTempIssue = "database.temp.issue" ActionTempRevoke = "database.temp.revoke" ActionTempExpire = "database.temp.expire" ActionGrantApply = "database.access.grant" ActionGrantRemove = "database.access.ungrant" ActionRulesApply = "database.network.rules.apply" ActionRulesRemove = "database.network.rules.remove" ActionMakePrivate = "database.network.make_private" ActionScopeSet = "database.network.scope.set" ActionTLSSet = "database.network.tls.set" ActionTLSToggle = "database.tls.toggle" )
Audit action names written to the audit log's method column. They never carry a credential, only the database and the subject role or principal.
const ( TLSRequired = "required" TLSOptional = "optional" )
TLS enforcement states as read from the server.
const ( EnvTTLMinMinutes = "APP_DB_TEMP_TTL_MIN_MINUTES" EnvTTLMaxMinutes = "APP_DB_TEMP_TTL_MAX_MINUTES" EnvTTLDefaultMinutes = "APP_DB_TEMP_TTL_DEFAULT_MINUTES" EnvSweepSeconds = "APP_DB_TEMP_SWEEP_SECONDS" // TempConnLimit caps concurrent sessions for a temporary role. TempConnLimit = 5 )
Environment variables that tune temporary credential lifetimes, in minutes.
const ListRolesSQL = `` /* 335-byte string literal not displayed */
ListRolesSQL selects every non-system role with its attributes and live connection count, as CSV.
const SystemActor = "database-access-sweeper"
SystemActor names the sweeper in audit rows.
const ( // TempRolePrefix marks roles minted by the temporary access path so a // sweeper pass and a human can tell them apart at a glance. TempRolePrefix = "tmp_" )
Variables ¶
var ( ErrRoleExists = errors.New("role already exists") ErrRoleNotFound = errors.New("role not found") ErrProtected = errors.New("role is protected") ErrTTL = errors.New("invalid ttl") )
Sentinel errors the API maps to status codes.
var ErrHealthUnsupported = fmt.Errorf("database health probe not supported for this engine")
ErrHealthUnsupported means no connection or disk probe exists for the engine.
var ErrPreset = errors.New("unknown preset")
ErrPreset is returned for an unknown preset.
var ( // ErrRoleName is wrapped by every role-name validation failure. ErrRoleName = errors.New("invalid role name") )
var ErrScopeUnplaced = errors.New("database has nothing to scope to")
ErrScopeUnplaced is returned when the database has no project or environment to scope to.
Functions ¶
func CreateRoleSQL ¶
func CreateRoleSQL(p CreateParams) (string, error)
CreateRoleSQL builds the transaction that creates the role and applies its preset. Every identifier is validated and quoted; the password is a verifier.
func DropRoleSQL ¶
DropRoleSQL ends the role's sessions, hands anything it owns to the admin role (never dropping data), strips its privileges and drops it. Safe to run again against a role that is already gone.
func GeneratePassword ¶
GeneratePassword returns a random URL-safe password.
func NewTempRoleName ¶
NewTempRoleName returns a fresh tmp_ role name.
func Protected ¶
Protected reports whether an existing role must never be altered or dropped through this package: the platform admin, any superuser, and system roles.
func QuoteLiteral ¶
QuoteLiteral single-quotes a Postgres string literal.
func ReadConnections ¶
func ReadConnections(ctx context.Context, ex Exec, containerID, engine string) (used, limit int, err error)
ReadConnections returns connections in use and the server limit for a running database container.
func ReadDiskUsedPercent ¶
ReadDiskUsedPercent returns how full the filesystem under the database's data directory is.
func ScramVerifier ¶
ScramVerifier hashes password into a Postgres SCRAM-SHA-256 verifier, so the SQL that sets it never carries the plaintext (a failed statement is logged verbatim by the server).
func SetLoginSQL ¶
SetLoginSQL enables or disables login. Disabling also ends open sessions.
func SweepIntervalFromEnv ¶
SweepIntervalFromEnv returns how often the sweeper runs.
func TempPresetAllowed ¶
TempPresetAllowed reports whether a temporary credential may use p. Owner is excluded: short-lived access should never be able to alter schema.
func ValidateRoleName ¶
ValidateRoleName checks name against the identifier rules and the names the platform and Postgres reserve. adminRole is the platform's own admin role for this database, which can never be recreated or shadowed.
Types ¶
type CreateParams ¶
type CreateParams struct {
Role string
Database string
AdminRole string
Preset Preset
Verifier string
ConnLimit int
ValidUntil time.Time
}
CreateParams describes a role to create. Verifier is a SCRAM verifier from ScramVerifier, never a plaintext password.
type CreateParamsIn ¶
type CreateParamsIn struct {
Role string
Preset Preset
ConnLimit int
ValidUntil time.Time
// Temporary skips the user-name reservation so a generated tmp_ name is accepted.
Temporary bool
}
CreateParamsIn is what a caller chooses; the rest is derived.
type Credential ¶
Credential is a freshly minted login. Password is shown once by the caller and never stored.
type Exec ¶
type Exec interface {
ExecWithInput(ctx context.Context, containerID string, cmd []string, stdin io.Reader) (io.ReadCloser, error)
}
Exec is the slice of the container runtime this package needs.
type Placement ¶
type Placement struct {
Name string `json:"name"`
ProjectID string `json:"project_id,omitempty"`
EnvironmentID string `json:"environment_id,omitempty"`
// ProjectName and EnvironmentName are only used to word reasons.
ProjectName string `json:"project_name,omitempty"`
EnvironmentName string `json:"environment_name,omitempty"`
}
Placement is where a database or an app sits in the platform's grouping.
type Postgres ¶
Postgres manages roles in one managed Postgres container. The platform's admin role and the database share a name, which is what Admin holds.
func (Postgres) Create ¶
func (p Postgres) Create(ctx context.Context, in CreateParamsIn) (Credential, error)
Create makes a login role with the preset's privileges.
func (Postgres) DropTemp ¶
DropTemp removes a temporary role without the existence listing, so the sweeper stays idempotent and cheap. Only tmp_ names are accepted.
func (Postgres) SetOwnerPassword ¶
SetOwnerPassword sets the platform admin role's password without ending sessions, so a rotation that fails verification can be undone cleanly.
func (Postgres) SetRequireTLS ¶
SetRequireTLS rewrites the catch-all network rules to hostssl (or back to host) and reloads the server. Local socket access, which the platform's own exec path uses, is untouched, so this cannot lock the control plane out.
type Preset ¶
type Preset string
Preset is a named privilege bundle on the database's public schema.
type Role ¶
type Role struct {
Name string `json:"name"`
CanLogin bool `json:"can_login"`
Superuser bool `json:"superuser"`
CreateDB bool `json:"create_db"`
CreateRole bool `json:"create_role"`
Replication bool `json:"replication"`
ConnLimit int `json:"connection_limit"`
ValidUntil string `json:"valid_until,omitempty"`
Connections int `json:"connections"`
}
Role is one database role as the engine reports it.
func ParseRoles ¶
ParseRoles turns ListRolesSQL's CSV rows into roles.
type Scope ¶
type Scope string
Scope is how far a database's network reach extends.
type StatementError ¶
type StatementError struct{ Message string }
StatementError is a database-side failure with only the engine's own ERROR lines kept, so no statement text is echoed back.
func (*StatementError) Error ¶
func (e *StatementError) Error() string
type StoreAdapter ¶
StoreAdapter exposes the store's access-user table as a SweepStore.
func (StoreAdapter) ClaimTempCredential ¶
func (a StoreAdapter) ClaimTempCredential(ctx context.Context, id string, now time.Time, lease time.Duration) (bool, error)
ClaimTempCredential implements SweepStore.
func (StoreAdapter) CompleteTempCredential ¶
CompleteTempCredential implements SweepStore.
func (StoreAdapter) DueTempCredentials ¶
func (a StoreAdapter) DueTempCredentials(ctx context.Context, now time.Time) ([]TempCredential, error)
DueTempCredentials implements SweepStore.
func (StoreAdapter) ReleaseTempCredential ¶
func (a StoreAdapter) ReleaseTempCredential(ctx context.Context, id string) error
ReleaseTempCredential implements SweepStore.
type SweepAuditor ¶
type SweepAuditor interface {
RecordRevoked(ctx context.Context, c TempCredential)
}
SweepAuditor records that the system revoked a credential.
type SweepStore ¶
type SweepStore interface {
// DueTempCredentials lists credentials past ExpiresAt that are not yet revoked.
DueTempCredentials(ctx context.Context, now time.Time) ([]TempCredential, error)
// ClaimTempCredential moves one to revoking. A stale claim from a
// crashed pass is reclaimable after lease. False means someone else has it.
ClaimTempCredential(ctx context.Context, id string, now time.Time, lease time.Duration) (bool, error)
CompleteTempCredential(ctx context.Context, id string, now time.Time) error
// ReleaseTempCredential returns a claimed credential for the next pass.
ReleaseTempCredential(ctx context.Context, id string) error
}
SweepStore is the persistence the sweeper needs. Claim must be an atomic compare-and-set so overlapping passes revoke each credential once.
type Sweeper ¶
type Sweeper struct {
Store SweepStore
Revoker Revoker
Auditor SweepAuditor
Logger *slog.Logger
Now func() time.Time
// Lease is how long a revoking claim is honoured before another pass retakes it.
Lease time.Duration
}
Sweeper revokes temporary credentials at expiry, on a timer and once at boot, so a control plane restart never extends a credential's life.
type TTLLimits ¶
TTLLimits bound how long a temporary credential may live.
func DefaultTTLLimits ¶
func DefaultTTLLimits() TTLLimits
DefaultTTLLimits returns the built-in 15 minute to 24 hour window.
func TTLLimitsFromEnv ¶
TTLLimitsFromEnv reads overrides through getenv, ignoring unparseable or inconsistent values.
type TempCredential ¶
type TempCredential struct {
ID string
Database string
Role string
Preset Preset
CreatedBy string
CreatedAt time.Time
ExpiresAt time.Time
}
TempCredential is one tracked short-lived login. Its secret is never held.
func FromStore ¶
func FromStore(r store.DatabaseAccessUser) TempCredential
FromStore converts a store record.
type Verdict ¶
type Verdict struct {
App Placement `json:"app"`
Allowed bool `json:"allowed"`
Reason string `json:"reason,omitempty"`
}
Verdict is one referencing app's fate under a scope.