From 0ce41a1b2d857a38282c29d1e962a0b30664f274 Mon Sep 17 00:00:00 2001 From: Jens L Date: Tue, 6 Jun 2023 21:40:19 +0200 Subject: [PATCH] providers/ldap: add StartTLS support (#5861) * providers/ldap: add StartTLS support Signed-off-by: Jens Langhammer * add starttls test Signed-off-by: Jens Langhammer * update form and docs Signed-off-by: Jens Langhammer * re-add tls server name Signed-off-by: Jens Langhammer * update release notes Signed-off-by: Jens Langhammer --------- Signed-off-by: Jens Langhammer --- go.mod | 2 +- go.sum | 4 +- internal/outpost/ldap/bind.go | 2 +- internal/outpost/ldap/bind/binder.go | 2 +- internal/outpost/ldap/bind/direct/bind.go | 2 +- internal/outpost/ldap/bind/direct/unbind.go | 2 +- internal/outpost/ldap/bind/memory/memory.go | 2 +- internal/outpost/ldap/entries.go | 2 +- internal/outpost/ldap/group/group.go | 2 +- internal/outpost/ldap/instance.go | 2 +- internal/outpost/ldap/ldap.go | 17 ++++--- internal/outpost/ldap/search.go | 40 ++++++++--------- internal/outpost/ldap/search/direct/base.go | 2 +- internal/outpost/ldap/search/direct/direct.go | 2 +- internal/outpost/ldap/search/memory/memory.go | 2 +- internal/outpost/ldap/search/request.go | 2 +- internal/outpost/ldap/search/searcher.go | 2 +- internal/outpost/ldap/server/base.go | 2 +- internal/outpost/ldap/unbind.go | 2 +- internal/outpost/ldap/utils/utils.go | 2 +- internal/outpost/ldap/utils/utils_group.go | 2 +- internal/outpost/ldap/utils/utils_user.go | 2 +- tests/e2e/test_provider_ldap.py | 28 ++++++++++++ .../admin/providers/ldap/LDAPProviderForm.ts | 17 +++++-- website/docs/providers/ldap/index.md | 6 ++- website/docs/releases/2023/v2023.6.md | 45 +++++++++++++++++++ website/docs/releases/_template.md | 6 +-- 27 files changed, 147 insertions(+), 54 deletions(-) create mode 100644 website/docs/releases/2023/v2023.6.md diff --git a/go.mod b/go.mod index 8901160f4..30fa667eb 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module goauthentik.io go 1.20 require ( + beryju.io/ldap v0.1.0 github.com/Netflix/go-env v0.0.0-20210215222557-e437a7e7f9fb github.com/coreos/go-oidc v2.2.1+incompatible github.com/garyburd/redigo v1.6.4 @@ -20,7 +21,6 @@ require ( github.com/gorilla/websocket v1.5.0 github.com/jellydator/ttlcache/v3 v3.0.1 github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 - github.com/nmcclain/ldap v0.0.0-20210720162743-7f8d1e44eeba github.com/pires/go-proxyproto v0.7.0 github.com/prometheus/client_golang v1.15.1 github.com/sirupsen/logrus v1.9.3 diff --git a/go.sum b/go.sum index 2290be92b..e635b2400 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +beryju.io/ldap v0.1.0 h1:rPjGE3qR1Klbvn9N+iECWdzt/tK87XHgz8W5wZJg9B8= +beryju.io/ldap v0.1.0/go.mod h1:sOrYV+ZlDTDu/IvIiEiuAaXzjcpMBE+XXr4V+NJ0pWI= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU= github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= @@ -165,8 +167,6 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 h1:D9EvfGQvlkKaDr2CRKN++7HbSXbefUNDrPq60T+g24s= github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484/go.mod h1:O1EljZ+oHprtxDDPHiMWVo/5dBT6PlvWX5PSwj80aBA= -github.com/nmcclain/ldap v0.0.0-20210720162743-7f8d1e44eeba h1:DO8NFYdcRv1dnyAINJIBm6Bw2XibtLvQniNFGzf2W8E= -github.com/nmcclain/ldap v0.0.0-20210720162743-7f8d1e44eeba/go.mod h1:4S0XndRL8HNOaQBfdViJ2F/GPCgL524xlXRuXFH12/U= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= diff --git a/internal/outpost/ldap/bind.go b/internal/outpost/ldap/bind.go index adb3589ae..cbf668662 100644 --- a/internal/outpost/ldap/bind.go +++ b/internal/outpost/ldap/bind.go @@ -3,8 +3,8 @@ package ldap import ( "net" + "beryju.io/ldap" "github.com/getsentry/sentry-go" - "github.com/nmcclain/ldap" "github.com/prometheus/client_golang/prometheus" log "github.com/sirupsen/logrus" "goauthentik.io/internal/outpost/ldap/bind" diff --git a/internal/outpost/ldap/bind/binder.go b/internal/outpost/ldap/bind/binder.go index ddf5414d6..4644b66e4 100644 --- a/internal/outpost/ldap/bind/binder.go +++ b/internal/outpost/ldap/bind/binder.go @@ -3,7 +3,7 @@ package bind import ( "context" - "github.com/nmcclain/ldap" + "beryju.io/ldap" ) type Binder interface { diff --git a/internal/outpost/ldap/bind/direct/bind.go b/internal/outpost/ldap/bind/direct/bind.go index 1b07c0a8f..0ec9649bb 100644 --- a/internal/outpost/ldap/bind/direct/bind.go +++ b/internal/outpost/ldap/bind/direct/bind.go @@ -3,8 +3,8 @@ package direct import ( "context" + "beryju.io/ldap" "github.com/getsentry/sentry-go" - "github.com/nmcclain/ldap" "github.com/prometheus/client_golang/prometheus" log "github.com/sirupsen/logrus" "goauthentik.io/internal/outpost/flow" diff --git a/internal/outpost/ldap/bind/direct/unbind.go b/internal/outpost/ldap/bind/direct/unbind.go index 39d64fb79..5dc6e322a 100644 --- a/internal/outpost/ldap/bind/direct/unbind.go +++ b/internal/outpost/ldap/bind/direct/unbind.go @@ -1,7 +1,7 @@ package direct import ( - "github.com/nmcclain/ldap" + "beryju.io/ldap" log "github.com/sirupsen/logrus" "goauthentik.io/internal/outpost/flow" "goauthentik.io/internal/outpost/ldap/bind" diff --git a/internal/outpost/ldap/bind/memory/memory.go b/internal/outpost/ldap/bind/memory/memory.go index aa6abbef6..ef5eb56a8 100644 --- a/internal/outpost/ldap/bind/memory/memory.go +++ b/internal/outpost/ldap/bind/memory/memory.go @@ -3,8 +3,8 @@ package memory import ( "time" + "beryju.io/ldap" ttlcache "github.com/jellydator/ttlcache/v3" - "github.com/nmcclain/ldap" log "github.com/sirupsen/logrus" "goauthentik.io/internal/outpost/ldap/bind" "goauthentik.io/internal/outpost/ldap/bind/direct" diff --git a/internal/outpost/ldap/entries.go b/internal/outpost/ldap/entries.go index 577027661..f339bae3c 100644 --- a/internal/outpost/ldap/entries.go +++ b/internal/outpost/ldap/entries.go @@ -5,7 +5,7 @@ import ( "strconv" "strings" - "github.com/nmcclain/ldap" + "beryju.io/ldap" "goauthentik.io/api/v3" "goauthentik.io/internal/outpost/ldap/constants" "goauthentik.io/internal/outpost/ldap/utils" diff --git a/internal/outpost/ldap/group/group.go b/internal/outpost/ldap/group/group.go index 0ad2d3915..abfdf987f 100644 --- a/internal/outpost/ldap/group/group.go +++ b/internal/outpost/ldap/group/group.go @@ -3,7 +3,7 @@ package group import ( "strconv" - "github.com/nmcclain/ldap" + "beryju.io/ldap" "goauthentik.io/api/v3" "goauthentik.io/internal/outpost/ldap/constants" "goauthentik.io/internal/outpost/ldap/server" diff --git a/internal/outpost/ldap/instance.go b/internal/outpost/ldap/instance.go index 646fa7f7b..6014e7ccc 100644 --- a/internal/outpost/ldap/instance.go +++ b/internal/outpost/ldap/instance.go @@ -6,8 +6,8 @@ import ( "strings" "sync" + "beryju.io/ldap" "github.com/go-openapi/strfmt" - "github.com/nmcclain/ldap" log "github.com/sirupsen/logrus" "goauthentik.io/api/v3" diff --git a/internal/outpost/ldap/ldap.go b/internal/outpost/ldap/ldap.go index 24a28e3a1..0bdd2a231 100644 --- a/internal/outpost/ldap/ldap.go +++ b/internal/outpost/ldap/ldap.go @@ -12,8 +12,9 @@ import ( "goauthentik.io/internal/crypto" "goauthentik.io/internal/outpost/ak" "goauthentik.io/internal/outpost/ldap/metrics" + "goauthentik.io/internal/utils" - "github.com/nmcclain/ldap" + "beryju.io/ldap" ) type LDAPServer struct { @@ -26,15 +27,21 @@ type LDAPServer struct { } func NewServer(ac *ak.APIController) *LDAPServer { - s := ldap.NewServer() - s.EnforceLDAP = true ls := &LDAPServer{ - s: s, log: log.WithField("logger", "authentik.outpost.ldap"), ac: ac, cs: ak.NewCryptoStore(ac.Client.CryptoApi), providers: []*ProviderInstance{}, } + 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) @@ -67,7 +74,7 @@ func (ls *LDAPServer) StartLDAPServer() error { return err } ls.log.WithField("listen", listen).Info("Stopping LDAP server") - return ls.s.ListenAndServe(listen) + return nil } func (ls *LDAPServer) Start() error { diff --git a/internal/outpost/ldap/search.go b/internal/outpost/ldap/search.go index d9fe1d4c6..59b29969c 100644 --- a/internal/outpost/ldap/search.go +++ b/internal/outpost/ldap/search.go @@ -5,9 +5,9 @@ import ( "net" "strings" + "beryju.io/ldap" "github.com/getsentry/sentry-go" goldap "github.com/go-ldap/ldap/v3" - "github.com/nmcclain/ldap" "github.com/prometheus/client_golang/prometheus" log "github.com/sirupsen/logrus" "goauthentik.io/internal/outpost/ldap/metrics" @@ -37,24 +37,24 @@ func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn n }() if searchReq.BaseDN == "" { - return ldap.ServerSearchResult{ - Entries: []*ldap.Entry{ - { - DN: "", - Attributes: []*ldap.EntryAttribute{ - { - Name: "objectClass", - Values: []string{"top", "OpenLDAProotDSE"}, - }, - { - Name: "subschemaSubentry", - Values: []string{"cn=subschema"}, - }, - }, - }, - }, - Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldap.LDAPResultSuccess, - }, nil + return ldap.ServerSearchResult{ + Entries: []*ldap.Entry{ + { + DN: "", + Attributes: []*ldap.EntryAttribute{ + { + Name: "objectClass", + Values: []string{"top", "OpenLDAProotDSE"}, + }, + { + Name: "subschemaSubentry", + Values: []string{"cn=subschema"}, + }, + }, + }, + }, + Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldap.LDAPResultSuccess, + }, nil } bd, err := goldap.ParseDN(strings.ToLower(searchReq.BaseDN)) if err != nil { @@ -68,7 +68,7 @@ func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn n return provider.searcher.Search(req) } } - return ldap.ServerSearchResult{ + return ldap.ServerSearchResult{ Entries: []*ldap.Entry{ { DN: "", diff --git a/internal/outpost/ldap/search/direct/base.go b/internal/outpost/ldap/search/direct/base.go index 6278ab6ab..4af0e1816 100644 --- a/internal/outpost/ldap/search/direct/base.go +++ b/internal/outpost/ldap/search/direct/base.go @@ -3,7 +3,7 @@ package direct import ( "fmt" - "github.com/nmcclain/ldap" + "beryju.io/ldap" "goauthentik.io/internal/constants" "goauthentik.io/internal/outpost/ldap/search" ) diff --git a/internal/outpost/ldap/search/direct/direct.go b/internal/outpost/ldap/search/direct/direct.go index ed67a6a33..c4a38db96 100644 --- a/internal/outpost/ldap/search/direct/direct.go +++ b/internal/outpost/ldap/search/direct/direct.go @@ -8,8 +8,8 @@ import ( log "github.com/sirupsen/logrus" "golang.org/x/sync/errgroup" + "beryju.io/ldap" "github.com/getsentry/sentry-go" - "github.com/nmcclain/ldap" "github.com/prometheus/client_golang/prometheus" "goauthentik.io/api/v3" "goauthentik.io/internal/outpost/ldap/constants" diff --git a/internal/outpost/ldap/search/memory/memory.go b/internal/outpost/ldap/search/memory/memory.go index 164ea1925..08011a6d6 100644 --- a/internal/outpost/ldap/search/memory/memory.go +++ b/internal/outpost/ldap/search/memory/memory.go @@ -6,8 +6,8 @@ import ( "fmt" "strings" + "beryju.io/ldap" "github.com/getsentry/sentry-go" - "github.com/nmcclain/ldap" "github.com/prometheus/client_golang/prometheus" log "github.com/sirupsen/logrus" "goauthentik.io/api/v3" diff --git a/internal/outpost/ldap/search/request.go b/internal/outpost/ldap/search/request.go index 10eeeccd8..fd4097527 100644 --- a/internal/outpost/ldap/search/request.go +++ b/internal/outpost/ldap/search/request.go @@ -6,9 +6,9 @@ import ( "net" "strings" + "beryju.io/ldap" "github.com/getsentry/sentry-go" "github.com/google/uuid" - "github.com/nmcclain/ldap" log "github.com/sirupsen/logrus" "goauthentik.io/internal/utils" ) diff --git a/internal/outpost/ldap/search/searcher.go b/internal/outpost/ldap/search/searcher.go index b9394a212..d3490b221 100644 --- a/internal/outpost/ldap/search/searcher.go +++ b/internal/outpost/ldap/search/searcher.go @@ -1,7 +1,7 @@ package search import ( - "github.com/nmcclain/ldap" + "beryju.io/ldap" ) type Searcher interface { diff --git a/internal/outpost/ldap/server/base.go b/internal/outpost/ldap/server/base.go index cc5dee89e..0650598dc 100644 --- a/internal/outpost/ldap/server/base.go +++ b/internal/outpost/ldap/server/base.go @@ -1,8 +1,8 @@ package server import ( + "beryju.io/ldap" "github.com/go-openapi/strfmt" - "github.com/nmcclain/ldap" "goauthentik.io/api/v3" "goauthentik.io/internal/outpost/ldap/flags" ) diff --git a/internal/outpost/ldap/unbind.go b/internal/outpost/ldap/unbind.go index 52c64f71e..3b0db3e5f 100644 --- a/internal/outpost/ldap/unbind.go +++ b/internal/outpost/ldap/unbind.go @@ -4,7 +4,7 @@ import ( "net" "github.com/getsentry/sentry-go" - "github.com/nmcclain/ldap" + "beryju.io/ldap" "github.com/prometheus/client_golang/prometheus" log "github.com/sirupsen/logrus" "goauthentik.io/internal/outpost/ldap/bind" diff --git a/internal/outpost/ldap/utils/utils.go b/internal/outpost/ldap/utils/utils.go index 1d203d1fb..237aa6f89 100644 --- a/internal/outpost/ldap/utils/utils.go +++ b/internal/outpost/ldap/utils/utils.go @@ -5,7 +5,7 @@ import ( "strconv" "strings" - "github.com/nmcclain/ldap" + "beryju.io/ldap" ldapConstants "goauthentik.io/internal/outpost/ldap/constants" ) diff --git a/internal/outpost/ldap/utils/utils_group.go b/internal/outpost/ldap/utils/utils_group.go index ff1528cc3..51a120593 100644 --- a/internal/outpost/ldap/utils/utils_group.go +++ b/internal/outpost/ldap/utils/utils_group.go @@ -3,9 +3,9 @@ package utils import ( "strings" + "beryju.io/ldap" goldap "github.com/go-ldap/ldap/v3" ber "github.com/nmcclain/asn1-ber" - "github.com/nmcclain/ldap" "goauthentik.io/api/v3" "goauthentik.io/internal/outpost/ldap/constants" ) diff --git a/internal/outpost/ldap/utils/utils_user.go b/internal/outpost/ldap/utils/utils_user.go index e2e47f22f..571fcd7ce 100644 --- a/internal/outpost/ldap/utils/utils_user.go +++ b/internal/outpost/ldap/utils/utils_user.go @@ -1,9 +1,9 @@ package utils import ( + "beryju.io/ldap" goldap "github.com/go-ldap/ldap/v3" ber "github.com/nmcclain/asn1-ber" - "github.com/nmcclain/ldap" "goauthentik.io/api/v3" "goauthentik.io/internal/outpost/ldap/constants" ) diff --git a/tests/e2e/test_provider_ldap.py b/tests/e2e/test_provider_ldap.py index 083493123..e22e68596 100644 --- a/tests/e2e/test_provider_ldap.py +++ b/tests/e2e/test_provider_ldap.py @@ -133,6 +133,34 @@ class TestProviderLDAP(SeleniumTestCase): ) ) + @retry() + @apply_blueprint( + "default/flow-default-authentication-flow.yaml", + "default/flow-default-invalidation-flow.yaml", + ) + def test_ldap_bind_success_starttls(self): + """Test simple bind with ssl""" + self._prepare() + server = Server("ldap://localhost:3389") + _connection = Connection( + server, + raise_exceptions=True, + user=f"cn={self.user.username},ou=users,DC=ldap,DC=goauthentik,DC=io", + password=self.user.username, + ) + _connection.start_tls() + _connection.bind() + self.assertTrue( + Event.objects.filter( + action=EventAction.LOGIN, + user={ + "pk": self.user.pk, + "email": self.user.email, + "username": self.user.username, + }, + ) + ) + @retry() @apply_blueprint( "default/flow-default-authentication-flow.yaml", diff --git a/web/src/admin/providers/ldap/LDAPProviderForm.ts b/web/src/admin/providers/ldap/LDAPProviderForm.ts index 977193daa..6c240b2c2 100644 --- a/web/src/admin/providers/ldap/LDAPProviderForm.ts +++ b/web/src/admin/providers/ldap/LDAPProviderForm.ts @@ -52,7 +52,6 @@ export class LDAPProviderFormPage extends ModelForm { lDAPProviderRequest: data, }); } else { - data.tlsServerName = ""; return new ProvidersApi(DEFAULT_CONFIG).providersLdapCreate({ lDAPProviderRequest: data, }); @@ -240,12 +239,24 @@ export class LDAPProviderFormPage extends ModelForm {

${msg( - "Due to protocol limitations, this certificate is only used when the outpost has a single provider, or all providers use the same certificate.", + "The certificate for the above configured Base DN. As a fallback, the provider uses a self-signed certificate.", )}

+ + +

${msg( - "If multiple providers share an outpost, a self-signed certificate is used.", + "DNS name for which the above configured certificate should be used. The certificate cannot be detected based on the base DN, as the SSL/TLS negotiation happens before such data is exchanged.", )}

diff --git a/website/docs/providers/ldap/index.md b/website/docs/providers/ldap/index.md index 30139774f..8aa11382e 100644 --- a/website/docs/providers/ldap/index.md +++ b/website/docs/providers/ldap/index.md @@ -56,11 +56,13 @@ Starting with 2021.9.1, custom attributes will override the inbuilt attributes. Starting with 2023.3, periods and slashes in custom attributes will be sanitized. ::: -## SSL +## SSL / StartTLS You can also configure SSL for your LDAP Providers by selecting a certificate and a server name in the provider settings. -This enables you to bind on port 636 using LDAPS, StartTLS is not supported. +Starting with authentik 2023.6, StartTLS is supported, and the provider will pick the correct certificate based on the DN a bind attempt is made with. + +This enables you to bind on port 636 using LDAPS. ## Integrations diff --git a/website/docs/releases/2023/v2023.6.md b/website/docs/releases/2023/v2023.6.md new file mode 100644 index 000000000..2ed98162c --- /dev/null +++ b/website/docs/releases/2023/v2023.6.md @@ -0,0 +1,45 @@ +--- +title: Release 2023.6 +slug: "/releases/2023.6" +--- + + + +## New features + +- LDAP StartTLS support + + authentik's [LDAP Provider](../../providers/ldap/index.md) now supports StartTLS in addition to supporting SSL. The StartTLS is a more modern method of encrypting LDAP traffic. With this added support, the LDAP [Outpost](../../outposts/index.mdx) can now support multiple certificates. + +## Upgrading + +This release does not introduce any new requirements. + +### docker-compose + +To upgrade, download the new docker-compose file and update the Docker stack with the new version, using these commands: + +``` +wget -O docker-compose.yml https://goauthentik.io/version/2023.6/docker-compose.yml +docker-compose up -d +``` + +The `-O` flag retains the downloaded file's name, overwriting any existing local file with the same name. + +### Kubernetes + +Update your values to use the new images: + +```yaml +image: + repository: ghcr.io/goauthentik/server + tag: 2023.6.0 +``` + +## Minor changes/fixes + + + +## API Changes + + diff --git a/website/docs/releases/_template.md b/website/docs/releases/_template.md index 65c5b61ad..cf2bbe731 100644 --- a/website/docs/releases/_template.md +++ b/website/docs/releases/_template.md @@ -3,7 +3,7 @@ title: Release xxxx.x slug: "/releases/xxxx.x" --- -## Breaking changes + ## New features @@ -34,8 +34,8 @@ image: ## Minor changes/fixes -_Insert the output of `make gen-changelog` here_ + ## API Changes -_Insert output of `make gen-diff` here_ +