* outpost: promote session end signal to non-provider specific Signed-off-by: Jens Langhammer <jens@goauthentik.io> * implement server-side logout in ldap Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix previous import Signed-off-by: Jens Langhammer <jens@goauthentik.io> * use better retry logic Signed-off-by: Jens Langhammer <jens@goauthentik.io> * log Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make more generic if we switch from ws to something else Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make it possible to e2e test WS Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix ldap session id Signed-off-by: Jens Langhammer <jens@goauthentik.io> * ok I actually need to go to bed this took me an hour to fix Signed-off-by: Jens Langhammer <jens@goauthentik.io> * format; add ldap test Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix leftover state Signed-off-by: Jens Langhammer <jens@goauthentik.io> * remove thread Signed-off-by: Jens Langhammer <jens@goauthentik.io> * use ws base for radius Signed-off-by: Jens Langhammer <jens@goauthentik.io> * separate test utils Signed-off-by: Jens Langhammer <jens@goauthentik.io> * rename Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix missing super calls Signed-off-by: Jens Langhammer <jens@goauthentik.io> * websocket tests with browser 🎉 Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add proxy test for sign out Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix install_id issue with channels tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix proxy basic auth test Signed-off-by: Jens Langhammer <jens@goauthentik.io> * big code dedupe Signed-off-by: Jens Langhammer <jens@goauthentik.io> * allow passing go build args Signed-off-by: Jens Langhammer <jens@goauthentik.io> * improve waiting for outpost Signed-off-by: Jens Langhammer <jens@goauthentik.io> * rewrite ldap tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * ok actually fix the tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * undo a couple things that need more time to cook Signed-off-by: Jens Langhammer <jens@goauthentik.io> * remove unused lockfile-lint dependency since we use a shell script and SFE does not have a lockfile Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix session id for ldap Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix missing createTimestamp and modifyTimestamp ldap attributes closes #10474 Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
		
			
				
	
	
		
			146 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			146 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package ldap
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"crypto/tls"
 | 
						|
	"net"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	"github.com/pires/go-proxyproto"
 | 
						|
	log "github.com/sirupsen/logrus"
 | 
						|
	"goauthentik.io/internal/config"
 | 
						|
	"goauthentik.io/internal/crypto"
 | 
						|
	"goauthentik.io/internal/outpost/ak"
 | 
						|
	"goauthentik.io/internal/outpost/ldap/metrics"
 | 
						|
	"goauthentik.io/internal/utils"
 | 
						|
 | 
						|
	"beryju.io/ldap"
 | 
						|
)
 | 
						|
 | 
						|
type LDAPServer struct {
 | 
						|
	s               *ldap.Server
 | 
						|
	log             *log.Entry
 | 
						|
	ac              *ak.APIController
 | 
						|
	cs              *ak.CryptoStore
 | 
						|
	defaultCert     *tls.Certificate
 | 
						|
	providers       []*ProviderInstance
 | 
						|
	connections     map[string]net.Conn
 | 
						|
	connectionsSync sync.Mutex
 | 
						|
}
 | 
						|
 | 
						|
func NewServer(ac *ak.APIController) ak.Outpost {
 | 
						|
	ls := &LDAPServer{
 | 
						|
		log:             log.WithField("logger", "authentik.outpost.ldap"),
 | 
						|
		ac:              ac,
 | 
						|
		cs:              ak.NewCryptoStore(ac.Client.CryptoApi),
 | 
						|
		providers:       []*ProviderInstance{},
 | 
						|
		connections:     map[string]net.Conn{},
 | 
						|
		connectionsSync: sync.Mutex{},
 | 
						|
	}
 | 
						|
	ac.AddEventHandler(ls.handleWSSessionEnd)
 | 
						|
	s := ldap.NewServer()
 | 
						|
	s.EnforceLDAP = true
 | 
						|
 | 
						|
	tlsConfig := utils.GetTLSConfig()
 | 
						|
	tlsConfig.GetCertificate = ls.getCertificates
 | 
						|
	s.StartTLS = tlsConfig
 | 
						|
 | 
						|
	ls.s = s
 | 
						|
 | 
						|
	defaultCert, err := crypto.GenerateSelfSignedCert()
 | 
						|
	if err != nil {
 | 
						|
		log.Warning(err)
 | 
						|
	}
 | 
						|
	ls.defaultCert = &defaultCert
 | 
						|
	s.BindFunc("", ls)
 | 
						|
	s.UnbindFunc("", ls)
 | 
						|
	s.SearchFunc("", ls)
 | 
						|
	s.CloseFunc("", ls)
 | 
						|
	return ls
 | 
						|
}
 | 
						|
 | 
						|
func (ls *LDAPServer) Type() string {
 | 
						|
	return "ldap"
 | 
						|
}
 | 
						|
 | 
						|
func (ls *LDAPServer) StartLDAPServer() error {
 | 
						|
	listen := config.Get().Listen.LDAP
 | 
						|
 | 
						|
	ln, err := net.Listen("tcp", listen)
 | 
						|
	if err != nil {
 | 
						|
		ls.log.WithField("listen", listen).WithError(err).Warning("Failed to listen (SSL)")
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	proxyListener := &proxyproto.Listener{Listener: ln, ConnPolicy: utils.GetProxyConnectionPolicy()}
 | 
						|
	defer func() {
 | 
						|
		err := proxyListener.Close()
 | 
						|
		if err != nil {
 | 
						|
			ls.log.WithError(err).Warning("failed to close proxy listener")
 | 
						|
		}
 | 
						|
	}()
 | 
						|
 | 
						|
	ls.log.WithField("listen", listen).Info("Starting LDAP server")
 | 
						|
	err = ls.s.Serve(proxyListener)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	ls.log.WithField("listen", listen).Info("Stopping LDAP server")
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (ls *LDAPServer) Start() error {
 | 
						|
	wg := sync.WaitGroup{}
 | 
						|
	wg.Add(3)
 | 
						|
	go func() {
 | 
						|
		defer wg.Done()
 | 
						|
		metrics.RunServer()
 | 
						|
	}()
 | 
						|
	go func() {
 | 
						|
		defer wg.Done()
 | 
						|
		err := ls.StartLDAPServer()
 | 
						|
		if err != nil {
 | 
						|
			panic(err)
 | 
						|
		}
 | 
						|
	}()
 | 
						|
	go func() {
 | 
						|
		defer wg.Done()
 | 
						|
		err := ls.StartLDAPTLSServer()
 | 
						|
		if err != nil {
 | 
						|
			panic(err)
 | 
						|
		}
 | 
						|
	}()
 | 
						|
	wg.Wait()
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (ls *LDAPServer) Stop() error {
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (ls *LDAPServer) TimerFlowCacheExpiry(ctx context.Context) {
 | 
						|
	for _, p := range ls.providers {
 | 
						|
		ls.log.WithField("flow", p.authenticationFlowSlug).Debug("Pre-heating flow cache")
 | 
						|
		p.binder.TimerFlowCacheExpiry(ctx)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (ls *LDAPServer) handleWSSessionEnd(ctx context.Context, msg ak.Event) error {
 | 
						|
	if msg.Instruction != ak.EventKindSessionEnd {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	mmsg := ak.EventArgsSessionEnd{}
 | 
						|
	err := msg.ArgsAs(&mmsg)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	ls.connectionsSync.Lock()
 | 
						|
	defer ls.connectionsSync.Unlock()
 | 
						|
	ls.log.Info("Disconnecting session due to session end event")
 | 
						|
	conn, ok := ls.connections[mmsg.SessionID]
 | 
						|
	if !ok {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	delete(ls.connections, mmsg.SessionID)
 | 
						|
	return conn.Close()
 | 
						|
}
 |