From ff24bc8cb8a7223d7306ba6b0e16d9807fb040ba Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sat, 21 Aug 2021 16:17:30 +0200 Subject: [PATCH] outpost/ldap: regularly pre-heat flow executor cache to increase bind performance Signed-off-by: Jens Langhammer --- internal/outpost/ak/api.go | 19 +++++++++++++------ internal/outpost/ak/api_ws.go | 2 +- internal/outpost/ak/outpost.go | 1 + internal/outpost/ak/periodical.go | 15 +++++++++++++++ internal/outpost/flow.go | 9 +++++++++ internal/outpost/ldap/bind.go | 7 +++++++ internal/outpost/ldap/instance_bind.go | 11 +++++++++++ internal/outpost/proxy/server.go | 2 ++ 8 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 internal/outpost/ak/periodical.go diff --git a/internal/outpost/ak/api.go b/internal/outpost/ak/api.go index da776a4e2..b6616ef2e 100644 --- a/internal/outpost/ak/api.go +++ b/internal/outpost/ak/api.go @@ -56,12 +56,6 @@ func NewAPIController(akURL url.URL, token string) *APIController { log := log.WithField("logger", "authentik.outpost.ak-api-controller") - akConfig, _, err := apiClient.RootApi.RootConfigRetrieve(context.Background()).Execute() - if err != nil { - log.WithError(err).Error("Failed to fetch global configuration") - return nil - } - // Because we don't know the outpost UUID, we simply do a list and pick the first // The service account this token belongs to should only have access to a single outpost outposts, _, err := apiClient.OutpostsApi.OutpostsInstancesList(context.Background()).Execute() @@ -73,6 +67,15 @@ func NewAPIController(akURL url.URL, token string) *APIController { outpost := outposts.Results[0] doGlobalSetup(outpost.Config) + log.WithField("name", outpost.Name).Debug("Fetched outpost configuration") + + akConfig, _, err := apiClient.RootApi.RootConfigRetrieve(context.Background()).Execute() + if err != nil { + log.WithError(err).Error("Failed to fetch global configuration") + return nil + } + log.Debug("Fetched global configuration") + ac := &APIController{ Client: apiClient, GlobalConfig: akConfig, @@ -121,5 +124,9 @@ func (a *APIController) StartBackgorundTasks() error { a.logger.Debug("Starting Interval updater...") a.startIntervalUpdater() }() + go func() { + a.logger.Debug("Starting periodical timer...") + a.startPeriodicalTasks() + }() return nil } diff --git a/internal/outpost/ak/api_ws.go b/internal/outpost/ak/api_ws.go index 6bdc41fcd..d66addfec 100644 --- a/internal/outpost/ak/api_ws.go +++ b/internal/outpost/ak/api_ws.go @@ -39,7 +39,7 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID strfmt.UUID) { } ws.Dial(fmt.Sprintf(pathTemplate, scheme, akURL.Host, outpostUUID.String()), header) - ac.logger.WithField("logger", "authentik.outpost.ak-ws").WithField("outpost", outpostUUID.String()).Debug("connecting to authentik") + ac.logger.WithField("logger", "authentik.outpost.ak-ws").WithField("outpost", outpostUUID.String()).Debug("Connecting to authentik") ac.wsConn = ws // Send hello message with our version diff --git a/internal/outpost/ak/outpost.go b/internal/outpost/ak/outpost.go index c60023757..dd59739e4 100644 --- a/internal/outpost/ak/outpost.go +++ b/internal/outpost/ak/outpost.go @@ -3,4 +3,5 @@ package ak type Outpost interface { Start() error Refresh() error + TimerFlowCacheExpiry() } diff --git a/internal/outpost/ak/periodical.go b/internal/outpost/ak/periodical.go new file mode 100644 index 000000000..fb35f446a --- /dev/null +++ b/internal/outpost/ak/periodical.go @@ -0,0 +1,15 @@ +package ak + +import ( + "time" +) + +func (a *APIController) startPeriodicalTasks() { + go a.Server.TimerFlowCacheExpiry() + go func() { + for range time.Tick(time.Duration(a.GlobalConfig.CacheTimeoutFlows) * time.Second) { + a.logger.WithField("timer", "cache-timeout").Debug("Running periodical tasks") + a.Server.TimerFlowCacheExpiry() + } + }() +} diff --git a/internal/outpost/flow.go b/internal/outpost/flow.go index dbcd47bb1..370e710e7 100644 --- a/internal/outpost/flow.go +++ b/internal/outpost/flow.go @@ -118,6 +118,15 @@ func (fe *FlowExecutor) getAnswer(stage StageComponent) string { return "" } +// WarmUp Ensure authentik's flow cache is warmed up +func (fe *FlowExecutor) WarmUp() error { + defer fe.sp.Finish() + gcsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.get_challenge") + req := fe.api.FlowsApi.FlowsExecutorGet(gcsp.Context(), fe.flowSlug).Query(fe.Params.Encode()) + _, _, err := req.Execute() + return err +} + func (fe *FlowExecutor) Execute() (bool, error) { return fe.solveFlowChallenge(1) } diff --git a/internal/outpost/ldap/bind.go b/internal/outpost/ldap/bind.go index c2f973647..de32efb16 100644 --- a/internal/outpost/ldap/bind.go +++ b/internal/outpost/ldap/bind.go @@ -48,3 +48,10 @@ func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LD req.log.WithField("request", "bind").Warning("No provider found for request") return ldap.LDAPResultOperationsError, nil } + +func (ls *LDAPServer) TimerFlowCacheExpiry() { + for _, p := range ls.providers { + ls.log.WithField("flow", p.flowSlug).Debug("Pre-heating flow cache") + p.TimerFlowCacheExpiry() + } +} diff --git a/internal/outpost/ldap/instance_bind.go b/internal/outpost/ldap/instance_bind.go index 42f0fb93e..83a364c03 100644 --- a/internal/outpost/ldap/instance_bind.go +++ b/internal/outpost/ldap/instance_bind.go @@ -118,3 +118,14 @@ func (pi *ProviderInstance) delayDeleteUserInfo(dn string) { } }() } + +func (pi *ProviderInstance) TimerFlowCacheExpiry() { + fe := outpost.NewFlowExecutor(context.Background(), pi.flowSlug, pi.s.ac.Client.GetConfig(), log.Fields{}) + fe.Params.Add("goauthentik.io/outpost/ldap", "true") + fe.Params.Add("goauthentik.io/outpost/ldap-warmup", "true") + + err := fe.WarmUp() + if err != nil { + pi.log.WithError(err).Warning("failed to warm up flow cache") + } +} diff --git a/internal/outpost/proxy/server.go b/internal/outpost/proxy/server.go index 27d735f0a..fb34f0ce0 100644 --- a/internal/outpost/proxy/server.go +++ b/internal/outpost/proxy/server.go @@ -60,6 +60,8 @@ func (s *Server) ServeHTTP() { s.logger.Printf("closing %s", listener.Addr()) } +func (s *Server) TimerFlowCacheExpiry() {} + func (s *Server) Handler(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/akprox/ping" { w.WriteHeader(204)