outposts/ldap: optimise backend Search API requests

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-07-22 20:38:30 +02:00
parent 8cd1223081
commit 0d0dcf8de0
6 changed files with 87 additions and 11 deletions

View file

@ -99,7 +99,7 @@ func (fe *FlowExecutor) DelegateClientIP(a net.Addr) {
func (fe *FlowExecutor) CheckApplicationAccess(appSlug string) (bool, error) { func (fe *FlowExecutor) CheckApplicationAccess(appSlug string) (bool, error) {
acsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.check_access") acsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.check_access")
defer acsp.Finish() defer acsp.Finish()
p, _, err := fe.api.CoreApi.CoreApplicationsCheckAccessRetrieve(context.Background(), appSlug).Execute() p, _, err := fe.api.CoreApi.CoreApplicationsCheckAccessRetrieve(acsp.Context(), appSlug).Execute()
if !p.Passing { if !p.Passing {
fe.log.Info("Access denied for user") fe.log.Info("Access denied for user")
return false, nil return false, nil
@ -125,8 +125,9 @@ func (fe *FlowExecutor) Execute() (bool, error) {
func (fe *FlowExecutor) solveFlowChallenge(depth int) (bool, error) { func (fe *FlowExecutor) solveFlowChallenge(depth int) (bool, error) {
defer fe.sp.Finish() defer fe.sp.Finish()
// Get challenge
gcsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.get_challenge") gcsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.get_challenge")
req := fe.api.FlowsApi.FlowsExecutorGet(context.Background(), fe.flowSlug).Query(fe.Params.Encode()) req := fe.api.FlowsApi.FlowsExecutorGet(gcsp.Context(), fe.flowSlug).Query(fe.Params.Encode())
challenge, _, err := req.Execute() challenge, _, err := req.Execute()
if err != nil { if err != nil {
return false, errors.New("failed to get challenge") return false, errors.New("failed to get challenge")
@ -137,7 +138,9 @@ func (fe *FlowExecutor) solveFlowChallenge(depth int) (bool, error) {
gcsp.SetTag("ak_component", ch.GetComponent()) gcsp.SetTag("ak_component", ch.GetComponent())
gcsp.Finish() gcsp.Finish()
responseReq := fe.api.FlowsApi.FlowsExecutorSolve(context.Background(), fe.flowSlug).Query(fe.Params.Encode()) // Resole challenge
scsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.solve_challenge")
responseReq := fe.api.FlowsApi.FlowsExecutorSolve(scsp.Context(), fe.flowSlug).Query(fe.Params.Encode())
switch ch.GetComponent() { switch ch.GetComponent() {
case string(StageIdentification): case string(StageIdentification):
responseReq = responseReq.FlowChallengeResponseRequest(api.IdentificationChallengeResponseRequestAsFlowChallengeResponseRequest(api.NewIdentificationChallengeResponseRequest(fe.getAnswer(StageIdentification)))) responseReq = responseReq.FlowChallengeResponseRequest(api.IdentificationChallengeResponseRequestAsFlowChallengeResponseRequest(api.NewIdentificationChallengeResponseRequest(fe.getAnswer(StageIdentification))))
@ -168,7 +171,6 @@ func (fe *FlowExecutor) solveFlowChallenge(depth int) (bool, error) {
return false, fmt.Errorf("unsupported challenge type %s", ch.GetComponent()) return false, fmt.Errorf("unsupported challenge type %s", ch.GetComponent())
} }
scsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.solve_challenge")
response, _, err := responseReq.Execute() response, _, err := responseReq.Execute()
ch = response.GetActualInstance().(ChallengeInt) ch = response.GetActualInstance().(ChallengeInt)
fe.log.WithField("component", ch.GetComponent()).WithField("type", ch.GetType()).Debug("Got response") fe.log.WithField("component", ch.GetComponent()).WithField("type", ch.GetType()).Debug("Got response")

View file

@ -23,7 +23,7 @@ type BindRequest struct {
func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) { func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) {
span := sentry.StartSpan(context.TODO(), "authentik.providers.ldap.bind", span := sentry.StartSpan(context.TODO(), "authentik.providers.ldap.bind",
sentry.TransactionName("authentik.providers.ldap.bind")) sentry.TransactionName("authentik.providers.ldap.bind"))
span.SetTag("user", bindDN) span.SetTag("user.username", bindDN)
defer span.Finish() defer span.Finish()
bindDN = strings.ToLower(bindDN) bindDN = strings.ToLower(bindDN)

View file

@ -1,7 +1,6 @@
package ldap package ldap
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
@ -34,8 +33,8 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult,
} }
pi.boundUsersMutex.RLock() pi.boundUsersMutex.RLock()
defer pi.boundUsersMutex.RUnlock()
flags, ok := pi.boundUsers[req.BindDN] flags, ok := pi.boundUsers[req.BindDN]
pi.boundUsersMutex.RUnlock()
if !ok { if !ok {
pi.log.Debug("User info not cached") pi.log.Debug("User info not cached")
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied") return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied")
@ -51,7 +50,7 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult,
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: unhandled filter type: %s [%s]", filterEntity, req.Filter) return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: unhandled filter type: %s [%s]", filterEntity, req.Filter)
case GroupObjectClass: case GroupObjectClass:
gapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_group") gapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_group")
groups, _, err := pi.s.ac.Client.CoreApi.CoreGroupsList(context.Background()).Execute() groups, _, err := parseFilterForGroup(pi.s.ac.Client.CoreApi.CoreGroupsList(gapisp.Context()), req.Filter).Execute()
gapisp.Finish() gapisp.Finish()
if err != nil { if err != nil {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("API Error: %s", err) return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("API Error: %s", err)
@ -62,7 +61,9 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult,
entries = append(entries, pi.GroupEntry(pi.APIGroupToLDAPGroup(g))) entries = append(entries, pi.GroupEntry(pi.APIGroupToLDAPGroup(g)))
} }
users, _, err := pi.s.ac.Client.CoreApi.CoreUsersList(context.Background()).Execute() uapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_user")
users, _, err := parseFilterForUser(pi.s.ac.Client.CoreApi.CoreUsersList(uapisp.Context()), req.Filter).Execute()
uapisp.Finish()
if err != nil { if err != nil {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("API Error: %s", err) return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("API Error: %s", err)
} }
@ -72,7 +73,7 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult,
} }
case UserObjectClass, "": case UserObjectClass, "":
uapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_user") uapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_user")
users, _, err := pi.s.ac.Client.CoreApi.CoreUsersList(context.Background()).Execute() users, _, err := parseFilterForUser(pi.s.ac.Client.CoreApi.CoreUsersList(uapisp.Context()), req.Filter).Execute()
uapisp.Finish() uapisp.Finish()
if err != nil { if err != nil {

View file

@ -0,0 +1,33 @@
package ldap
import (
ber "github.com/nmcclain/asn1-ber"
"github.com/nmcclain/ldap"
"goauthentik.io/api"
)
func parseFilterForGroup(req api.ApiCoreGroupsListRequest, filter string) api.ApiCoreGroupsListRequest {
f, err := ldap.CompileFilter(filter)
if err != nil {
return req
}
switch f.Tag {
case ldap.FilterEqualityMatch:
return parseFilterForGroupSingle(req, f)
case ldap.FilterAnd:
for _, child := range f.Children {
req = parseFilterForGroupSingle(req, child)
}
return req
}
return req
}
func parseFilterForGroupSingle(req api.ApiCoreGroupsListRequest, f *ber.Packet) api.ApiCoreGroupsListRequest {
v := f.Children[1].Value.(string)
switch f.Children[0].Value.(string) {
case "cn":
return req.Name(v)
}
return req
}

View file

@ -0,0 +1,38 @@
package ldap
import (
ber "github.com/nmcclain/asn1-ber"
"github.com/nmcclain/ldap"
"goauthentik.io/api"
)
func parseFilterForUser(req api.ApiCoreUsersListRequest, filter string) api.ApiCoreUsersListRequest {
f, err := ldap.CompileFilter(filter)
if err != nil {
return req
}
switch f.Tag {
case ldap.FilterEqualityMatch:
return parseFilterForUserSingle(req, f)
case ldap.FilterAnd:
for _, child := range f.Children {
req = parseFilterForUserSingle(req, child)
}
return req
}
return req
}
func parseFilterForUserSingle(req api.ApiCoreUsersListRequest, f *ber.Packet) api.ApiCoreUsersListRequest {
v := f.Children[1].Value.(string)
switch f.Children[0].Value.(string) {
case "cn":
return req.Username(v)
case "name":
case "displayName":
return req.Name(v)
case "mail":
return req.Email(v)
}
return req
}

View file

@ -24,7 +24,9 @@ type SearchRequest struct {
func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (ldap.ServerSearchResult, error) { func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (ldap.ServerSearchResult, error) {
span := sentry.StartSpan(context.TODO(), "authentik.providers.ldap.search", sentry.TransactionName("authentik.providers.ldap.search")) span := sentry.StartSpan(context.TODO(), "authentik.providers.ldap.search", sentry.TransactionName("authentik.providers.ldap.search"))
span.SetTag("user", bindDN) span.SetTag("user.username", bindDN)
span.SetTag("ak_filter", searchReq.Filter)
span.SetTag("ak_base_dn", searchReq.BaseDN)
defer span.Finish() defer span.Finish()