Documentation
¶
Overview ¶
Package mainbackend is the main backend, the process behind `dexafix serve api`. It owns the database, the keys and every security decision: the internal API that the frontend calls, the public API (/api/v1), the importers and the maintenance jobs. It never renders a page, and it does not import the frontend. (The directory is pkg/backend/main; a package cannot be named main and be imported.)
Index ¶
- Variables
- func ParseBaseURL(raw string) (*url.URL, error)
- func ParseTrustedProxies(items []string) ([]netip.Prefix, error)
- type Backend
- func (s *Backend) APIHandler() http.Handler
- func (s *Backend) Close()
- func (s *Backend) DisableInternalBudget()
- func (s *Backend) InternalHandler() http.Handler
- func (s *Backend) OnSettingsChange(f func())
- func (s *Backend) PublicHandler() http.Handler
- func (s *Backend) Ready(ctx context.Context) error
- func (s *Backend) ReloadSettings(ctx context.Context) error
- func (s *Backend) SetBaseURL(u *url.URL)
- func (s *Backend) SetServiceSecrets(secrets ...string)
- func (s *Backend) SetTrustedProxies(p []netip.Prefix)
- func (s *Backend) SetWorkerSecrets(secrets ...string)
- func (s *Backend) StartMaintenance()
- func (s *Backend) StartSettingsPoll()
- func (s *Backend) WrapListener(ln net.Listener) net.Listener
- type Option
- type SessionState
- type StateServer
- type StateStore
Constants ¶
This section is empty.
Variables ¶
var NetworkSettingNames = []string{settingBaseURL, settingTrustedProxies, settingProxyProtocol}
NetworkSettingNames are the settings that can make the Admin page unreachable when they are wrong (the base URL, the trusted proxies, the PROXY protocol). `dexafix settings reset-network` clears them.
Functions ¶
func ParseBaseURL ¶
ParseBaseURL checks the public address of the app. See edge.ParseBaseURL.
Types ¶
type Backend ¶
type Backend struct {
// Version is shown on the admin page.
Version string
// ForcePasswordLogin keeps password sign-in on regardless of the admin setting, to recover
// from a lockout. It is a flag because it is needed when nobody can reach the Admin page.
ForcePasswordLogin bool
// contains filtered or unexported fields
}
Backend is the half of the application that owns the database, the keys and every security decision. It never renders a page. Nothing in the frontend can reach its fields: the Frontend is another type, and the two meet only in the internal API (see internal.go).
func NewBackend ¶
NewBackend builds a backend: the database side only. The internal API serves a frontend (see SetServiceSecrets and InternalHandler), and PublicHandler serves /api/v1.
Any number of backends may share one database; exactly one of them should run the maintenance (StartMaintenance) and the others should be made WithoutCertificateGeneration.
func (*Backend) APIHandler ¶
APIHandler serves the public API, authenticated, without the load budget and the edge handling of PublicHandler: the combined process mounts it on the pages' handler, which does both.
func (*Backend) Close ¶
func (s *Backend) Close()
Close cancels the background jobs and waits for them to stop. Call it after the HTTP servers have been shut down.
func (*Backend) DisableInternalBudget ¶
func (s *Backend) DisableInternalBudget()
DisableInternalBudget removes the load budget of the internal API, and of the worker endpoints. The combined process uses it: there is no flood protection between the halves of one process.
func (*Backend) InternalHandler ¶
InternalHandler returns the handler of the internal API.
func (*Backend) OnSettingsChange ¶
func (s *Backend) OnSettingsChange(f func())
OnSettingsChange sets a function that is called after the settings in force change. The combined process uses it to pass a changed setting on to the frontend at once. Set it before serving.
func (*Backend) PublicHandler ¶
PublicHandler serves the public API, /api/v1, and /healthz. Everything else is not found: pages belong to the frontend.
func (*Backend) Ready ¶
Ready reports whether the backend can do its work: the database answers. It is the readiness check of the health listener. The body service and the workers are not part of it: the pages say so themselves when the body service is down, and everything else keeps working.
func (*Backend) ReloadSettings ¶
ReloadSettings reads the administrator settings from the database again and puts them in force. A running backend does this by itself (StartSettingsPoll) when another process changes them; it is for a caller that changed the database behind the backend's back and cannot wait for the poll.
func (*Backend) SetBaseURL ¶
SetBaseURL and SetTrustedProxies put a value in force without storing it. They exist for tests; the application changes these settings through the Admin page.
func (*Backend) SetServiceSecrets ¶
SetServiceSecrets sets the secrets the internal API accepts. The first is the one the frontend sends; the others are accepted so that a secret can be rotated without downtime.
func (*Backend) SetTrustedProxies ¶
func (*Backend) SetWorkerSecrets ¶
SetWorkerSecrets sets the secrets the worker endpoints accept. The first is the one workers send; the others are accepted so that a secret can be rotated without downtime. Without any, the worker endpoints refuse every call.
func (*Backend) StartMaintenance ¶
func (s *Backend) StartMaintenance()
StartMaintenance purges audit events older than the retention period now and every few hours, until Close. The period is an Admin setting read at every round; zero keeps the log for good.
func (*Backend) StartSettingsPoll ¶
func (s *Backend) StartSettingsPoll()
StartSettingsPoll reloads the settings when another process (a second backend, or the `settings reset-network` command) changed them, until Close.
type Option ¶
type Option func(*Backend)
Option changes how NewBackend builds a backend.
func WithBody ¶
WithBody gives the backend the body service to call. A backend cannot be built without one.
func WithState ¶
func WithState(v StateServer) Option
WithState keeps the failed-attempt counters, the single-use nonces and the sessions on a server of their own (Valkey) instead of in the database, so that a flood of wrong passwords costs the database nothing and a signed-in visitor costs it no session lookup. It fails closed like the database does: when the server does not answer, every address counts as locked out and every nonce as spent. The server must not evict keys (maxmemory-policy noeviction), or a counter or nonce could vanish and let an attacker try again or replay a value. /status shows it, and Close of the backend does not close it: the caller owns it.
func WithoutCertificateGeneration ¶
func WithoutCertificateGeneration() Option
WithoutCertificateGeneration makes the backend only load the stored self-signed certificate. It never generates or renews one by itself (the maintenance backend does); only an administrator's explicit TLS change on the Admin page still generates one. For backends that run with --maintenance=false.
type SessionState ¶
type SessionState interface {
CreateSession(ctx context.Context, tokenHash string, userID int64, method string, expires time.Time) error
GetSession(ctx context.Context, tokenHash string) (models.Session, error)
DeleteSession(ctx context.Context, tokenHash string) error
DeleteUserSessions(ctx context.Context, userID int64) error
CountSessions(ctx context.Context) (int64, error)
}
SessionState holds the sessions on a server of its own (Valkey), in place of the sessions table. It is what store.Store offers for sessions, except that it knows nothing about users: the backend reads the user (name, role, whether they exist) from the database on every request.
type StateServer ¶
type StateServer interface {
StateStore
SessionState
Ping(ctx context.Context) error
}
StateServer is a StateStore and a SessionState on a server of its own, which can also be asked whether it answers.
type StateStore ¶
type StateStore interface {
RecordFailure(ctx context.Context, key string, at time.Time) error
CountFailures(ctx context.Context, key string, since time.Time) (int, error)
ClearFailures(ctx context.Context, key string) error
UseNonce(ctx context.Context, nonce string, expires time.Time) (first bool, err error)
NonceSeen(ctx context.Context, nonce string) (bool, error)
}
StateStore holds the state that must agree across backend processes and is touched on every sign-in: the failed-attempt counters and the single-use nonces. The database holds it unless a Valkey server is configured (WithState), which keeps a flood of wrong passwords off the database. store.Store and valkeystate.Client both satisfy it.
Source Files
¶
- analytics.go
- api.go
- audit.go
- auth.go
- backend.go
- body.go
- consts.go
- estimates.go
- export.go
- hardening.go
- import.go
- internal.go
- internal_admin.go
- internal_admin_workers.go
- internal_config.go
- internal_integrations.go
- internal_measure.go
- internal_passkey.go
- internal_security.go
- internal_settings.go
- internal_signin.go
- internal_signin_steps.go
- internal_twofactor.go
- internal_worker.go
- provider.go
- proxy.go
- queue.go
- reconcile.go
- reset.go
- scheduler.go
- sealed.go
- security.go
- serversettings.go
- sessions.go
- status.go
- strengthlevel.go
- tlsserve.go
- totp.go
- validate.go
- views.go
- workerprovider_strengthlevel.go
- workerprovider_withings.go
- workerproviders.go
- workersettings.go