Skip to content

Commit

Permalink
feat(configuration): glauth ldap implementation (#4499)
Browse files Browse the repository at this point in the history
This adds a GLAuth LDAP implementation which purely adds sane defaults for GLAuth. There are no functional differences just when the implementation option is set to 'glauth' sane defaults which should be sufficient for most use cases are set. See the documentation at https://www.authelia.com/r/ldap#defaults for more details.
  • Loading branch information
james-d-elliott committed Dec 21, 2022
1 parent 5b8b314 commit d3d87ff
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 16 deletions.
33 changes: 20 additions & 13 deletions docs/content/en/reference/guides/ldap.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,14 @@ The following implementations exist:
- `lldap`:
- Specific configuration defaults for [lldap]
- No special implementation details
- `glauth`:
- Specific configuration defaults for [GLAuth]
- No special implementation details

[Active Directory]: https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/active-directory-domain-services
[FreeIPA]: https://www.freeipa.org/
[lldap]: https://github.com/nitnelave/lldap
[GLAuth]: https://glauth.github.io/

### Filter replacements

Expand Down Expand Up @@ -115,28 +119,31 @@ Username column.
| activedirectory | sAMAccountName | displayName | mail | cn |
| freeipa | uid | displayName | mail | cn |
| lldap | uid | cn | mail | cn |
| glauth | cn | description | mail | cn |

#### Filter defaults

The filters are probably the most important part to get correct when setting up LDAP. You want to exclude accounts under
the following conditions:

- The account is disabled or locked:
- The Active Directory implementation achieves this via the `(!(userAccountControl:1.2.840.113556.1.4.803:=2))` filter.
- The FreeIPA implementation achieves this via the `(!(nsAccountLock=TRUE))` filter.
- The [Active Directory] implementation achieves this via the `(!(userAccountControl:1.2.840.113556.1.4.803:=2))` filter.
- The [FreeIPA] implementation achieves this via the `(!(nsAccountLock=TRUE))` filter.
- The [GLAuth] implementation achieves this via the `(!(accountStatus=inactive))` filter.
- Their password is expired:
- The Active Directory implementation achieves this via the `(!(pwdLastSet=0))` filter.
- The FreeIPA implementation achieves this via the `(krbPasswordExpiration>={date-time:generalized})` filter.
- The [Active Directory] implementation achieves this via the `(!(pwdLastSet=0))` filter.
- The [FreeIPA] implementation achieves this via the `(krbPasswordExpiration>={date-time:generalized})` filter.
- Their account is expired:
- The Active Directory implementation achieves this via the `(|(!(accountExpires=*))(accountExpires=0)(accountExpires>={date-time:msft-nt-epoch}))` filter.
- The FreeIPA implementation achieves this via the `(|(!(krbPrincipalExpiration=*))(krbPrincipalExpiration>={date-time:generalized}))` filter.

| Implementation | Users Filter | Groups Filter |
|:---------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------:|
| custom | N/A | N/A |
| activedirectory | (&(|({username_attribute}={input})({mail_attribute}={input}))(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(pwdLastSet=0))(|(!(accountExpires=*))(accountExpires=0)(accountExpires>={date-time:msft-nt-epoch}))) | (&(member={dn})(|(sAMAccountType=268435456)(sAMAccountType=536870912))) |
| freeipa | (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)(!(nsAccountLock=TRUE))(krbPasswordExpiration>={date-time:generalized})(|(!(krbPrincipalExpiration=*))(krbPrincipalExpiration>={date-time:generalized}))) | (&(member={dn})(objectClass=groupOfNames)) |
| lldap | (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)) | (&(member={dn})(objectClass=groupOfNames)) |
- The [Active Directory] implementation achieves this via the `(|(!(accountExpires=*))(accountExpires=0)(accountExpires>={date-time:msft-nt-epoch}))` filter.
- The [FreeIPA] implementation achieves this via the `(|(!(krbPrincipalExpiration=*))(krbPrincipalExpiration>={date-time:generalized}))` filter.

| Implementation | Users Filter | Groups Filter |
|:---------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------:|
| custom | N/A | N/A |
| activedirectory | (&(|({username_attribute}={input})({mail_attribute}={input}))(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(pwdLastSet=0))(|(!(accountExpires=*))(accountExpires=0)(accountExpires>={date-time:msft-nt-epoch}))) | (&(member={dn})(sAMAccountType=268435456)) |
| freeipa | (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)(!(nsAccountLock=TRUE))(krbPasswordExpiration>={date-time:generalized})(|(!(krbPrincipalExpiration=*))(krbPrincipalExpiration>={date-time:generalized}))) | (&(member={dn})(objectClass=groupOfNames)) |
| lldap | (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)) | (&(member={dn})(objectClass=groupOfNames)) |
| glauth | (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=posixAccount)(!(accountStatus=inactive))) | (&(uniqueMember={dn})(objectClass=posixGroup)) |

##### Microsoft Active Directory sAMAccountType

Expand Down
14 changes: 14 additions & 0 deletions internal/configuration/schema/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,17 @@ var DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP = LDAPAuthe
MinimumVersion: TLSVersion{tls.VersionTLS12},
},
}

// DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth represents the default LDAP config for the LDAPImplementationGLAuth Implementation.
var DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth = LDAPAuthenticationBackend{
UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=posixAccount)(!(accountStatus=inactive)))",
UsernameAttribute: ldapAttrCommonName,
MailAttribute: ldapAttrMail,
DisplayNameAttribute: ldapAttrDescription,
GroupsFilter: "(&(uniqueMember={dn})(objectClass=posixGroup))",
GroupNameAttribute: ldapAttrCommonName,
Timeout: time.Second * 5,
TLS: &TLSConfig{
MinimumVersion: TLSVersion{tls.VersionTLS12},
},
}
4 changes: 4 additions & 0 deletions internal/configuration/schema/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ const (

// LDAPImplementationLLDAP is the string for the lldap LDAP implementation.
LDAPImplementationLLDAP = "lldap"

// LDAPImplementationGLAuth is the string for the GLAuth LDAP implementation.
LDAPImplementationGLAuth = "glauth"
)

// TOTP Algorithm.
Expand Down Expand Up @@ -110,5 +113,6 @@ const (
ldapAttrMail = "mail"
ldapAttrUserID = "uid"
ldapAttrDisplayName = "displayName"
ldapAttrDescription = "description"
ldapAttrCommonName = "cn"
)
2 changes: 2 additions & 0 deletions internal/configuration/validator/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,8 @@ func validateLDAPAuthenticationBackend(config *schema.AuthenticationBackend, val
implementation = &schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA
case schema.LDAPImplementationLLDAP:
implementation = &schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP
case schema.LDAPImplementationGLAuth:
implementation = &schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth
default:
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendImplementation, config.LDAP.Implementation, strings.Join(validLDAPImplementations, "', '")))
}
Expand Down
114 changes: 111 additions & 3 deletions internal/configuration/validator/authentication_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,12 @@ func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldSetActiveDirec
suite.Assert().Equal(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.AdditionalGroupsDN,
suite.config.LDAP.AdditionalGroupsDN)
suite.Assert().Equal(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.AdditionalUsersDN,
suite.config.LDAP.AdditionalUsersDN)
suite.Assert().Equal(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.AdditionalGroupsDN,
suite.config.LDAP.AdditionalGroupsDN)
suite.Assert().Equal(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.UsersFilter,
suite.config.LDAP.UsersFilter)
Expand Down Expand Up @@ -1153,9 +1159,9 @@ func (suite *LLDAPAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotManu
suite.config.LDAP.UsersFilter = "(&({username_attribute}={input})(objectClass=Person)(!(nsAccountLock=TRUE)))"
suite.config.LDAP.UsernameAttribute = "username"
suite.config.LDAP.MailAttribute = "m"
suite.config.LDAP.DisplayNameAttribute = "given"
suite.config.LDAP.GroupsFilter = "(&(member={dn})(objectClass=posixGroup))"
suite.config.LDAP.GroupNameAttribute = "grp"
suite.config.LDAP.DisplayNameAttribute = "fn"
suite.config.LDAP.GroupsFilter = "(&(member={dn})(!(objectClass=posixGroup)))"
suite.config.LDAP.GroupNameAttribute = "grpz"
suite.config.LDAP.AdditionalUsersDN = "OU=no"
suite.config.LDAP.AdditionalGroupsDN = "OU=yes"

Expand Down Expand Up @@ -1196,3 +1202,105 @@ func (suite *LLDAPAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotManu
func TestLLDAPAuthenticationBackend(t *testing.T) {
suite.Run(t, new(LLDAPAuthenticationBackendSuite))
}

type GLAuthAuthenticationBackendSuite struct {
suite.Suite
config schema.AuthenticationBackend
validator *schema.StructValidator
}

func (suite *GLAuthAuthenticationBackendSuite) SetupTest() {
suite.validator = schema.NewStructValidator()
suite.config = schema.AuthenticationBackend{}
suite.config.LDAP = &schema.LDAPAuthenticationBackend{}
suite.config.LDAP.Implementation = schema.LDAPImplementationGLAuth
suite.config.LDAP.URL = testLDAPURL
suite.config.LDAP.User = testLDAPUser
suite.config.LDAP.Password = testLDAPPassword
suite.config.LDAP.BaseDN = testLDAPBaseDN
suite.config.LDAP.TLS = schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.TLS
}

func (suite *GLAuthAuthenticationBackendSuite) TestShouldSetDefaults() {
ValidateAuthenticationBackend(&suite.config, suite.validator)

suite.Assert().Len(suite.validator.Warnings(), 0)
suite.Assert().Len(suite.validator.Errors(), 0)

suite.Assert().Equal(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.Timeout,
suite.config.LDAP.Timeout)
suite.Assert().Equal(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.AdditionalUsersDN,
suite.config.LDAP.AdditionalUsersDN)
suite.Assert().Equal(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.AdditionalGroupsDN,
suite.config.LDAP.AdditionalGroupsDN)
suite.Assert().Equal(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.UsersFilter,
suite.config.LDAP.UsersFilter)
suite.Assert().Equal(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.UsernameAttribute,
suite.config.LDAP.UsernameAttribute)
suite.Assert().Equal(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.DisplayNameAttribute,
suite.config.LDAP.DisplayNameAttribute)
suite.Assert().Equal(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.MailAttribute,
suite.config.LDAP.MailAttribute)
suite.Assert().Equal(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.GroupsFilter,
suite.config.LDAP.GroupsFilter)
suite.Assert().Equal(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.GroupNameAttribute,
suite.config.LDAP.GroupNameAttribute)
}

func (suite *GLAuthAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotManuallyConfigured() {
suite.config.LDAP.Timeout = time.Second * 2
suite.config.LDAP.UsersFilter = "(&({username_attribute}={input})(objectClass=Person)(!(accountStatus=inactive)))"
suite.config.LDAP.UsernameAttribute = "description"
suite.config.LDAP.MailAttribute = "sender"
suite.config.LDAP.DisplayNameAttribute = "given"
suite.config.LDAP.GroupsFilter = "(&(member={dn})(objectClass=posixGroup))"
suite.config.LDAP.GroupNameAttribute = "grp"
suite.config.LDAP.AdditionalUsersDN = "OU=users,OU=GlAuth"
suite.config.LDAP.AdditionalGroupsDN = "OU=groups,OU=GLAuth"

ValidateAuthenticationBackend(&suite.config, suite.validator)

suite.Assert().NotEqual(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.Timeout,
suite.config.LDAP.Timeout)
suite.Assert().NotEqual(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.AdditionalUsersDN,
suite.config.LDAP.AdditionalUsersDN)
suite.Assert().NotEqual(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.AdditionalGroupsDN,
suite.config.LDAP.AdditionalGroupsDN)
suite.Assert().NotEqual(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.Timeout,
suite.config.LDAP.Timeout)
suite.Assert().NotEqual(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.UsersFilter,
suite.config.LDAP.UsersFilter)
suite.Assert().NotEqual(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.UsernameAttribute,
suite.config.LDAP.UsernameAttribute)
suite.Assert().NotEqual(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.DisplayNameAttribute,
suite.config.LDAP.DisplayNameAttribute)
suite.Assert().NotEqual(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.MailAttribute,
suite.config.LDAP.MailAttribute)
suite.Assert().NotEqual(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.GroupsFilter,
suite.config.LDAP.GroupsFilter)
suite.Assert().NotEqual(
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationGLAuth.GroupNameAttribute,
suite.config.LDAP.GroupNameAttribute)
}

func TestGLAuthAuthenticationBackend(t *testing.T) {
suite.Run(t, new(GLAuthAuthenticationBackendSuite))
}

0 comments on commit d3d87ff

Please sign in to comment.