feat(configuration): lldap implementation (#4498)
This adds a lldap LDAP implementation which purely adds sane defaults for lldap. There are no functional differences just when the implementation option is set to 'lldap' 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.pull/4499/head^2
parent
d67554ab88
commit
5b8b3145ad
|
@ -286,6 +286,7 @@ authentication_backend:
|
|||
## Acceptable options are as follows:
|
||||
## - 'activedirectory' - for Microsoft Active Directory.
|
||||
## - 'freeipa' - for FreeIPA.
|
||||
## - 'lldap' - for lldap.
|
||||
## - 'custom' - for custom specifications of attributes and filters.
|
||||
## This currently defaults to 'custom' to maintain existing behaviour.
|
||||
##
|
||||
|
|
|
@ -48,10 +48,24 @@ Authelia primarily supports this method.
|
|||
|
||||
## Implementation Guide
|
||||
|
||||
There are currently two implementations, `custom`, `activedirectory`, and `freeipa`. The `activedirectory`
|
||||
implementation must be used if you wish to allow users to change or reset their password as Active Directory
|
||||
uses a custom attribute and mechanism for this. The long term intention of this is to have logical defaults for various
|
||||
RFC implementations of LDAP.
|
||||
The following implementations exist:
|
||||
|
||||
- `custom`:
|
||||
- Not specific to any particular LDAP provider
|
||||
- `activedirectory`:
|
||||
- Specific configuration defaults for [Active Directory]
|
||||
- Special implementation details:
|
||||
- Includes a special encoding format required for changing passwords with [Active Directory]
|
||||
- `freeipa`:
|
||||
- Specific configuration defaults for [FreeIPA]
|
||||
- No special implementation details
|
||||
- `lldap`:
|
||||
- Specific configuration defaults for [lldap]
|
||||
- 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
|
||||
|
||||
### Filter replacements
|
||||
|
||||
|
@ -61,14 +75,14 @@ search.
|
|||
#### Users filter replacements
|
||||
|
||||
| Placeholder | Phase | Replacement |
|
||||
|:-------------------------:|:-------:|:--------------------------------------------------------------------------------------------------------------:|
|
||||
|:-------------------------:|:-------:|:----------------------------------------------------------------------------------------------------------------:|
|
||||
| {username_attribute} | startup | The configured username attribute |
|
||||
| {mail_attribute} | startup | The configured mail attribute |
|
||||
| {display_name_attribute} | startup | The configured display name attribute |
|
||||
| {input} | search | The input into the username field |
|
||||
| {date-time:generalized} | search | The current UTC time formatted as a LDAP generalized time in the format of `20060102150405.0Z` |
|
||||
| {date-time:unix-epoch} | search | The current time formatted as a Unix epoch |
|
||||
| {date-time:msft-nt-epoch} | search | The current time formatted as a Microsoft NT epoch which is used by some Microsoft Active Directory attributes |
|
||||
| {date-time:msft-nt-epoch} | search | The current time formatted as a Microsoft NT epoch which is used by some Microsoft [Active Directory] attributes |
|
||||
|
||||
#### Groups filter replacements
|
||||
|
||||
|
@ -82,6 +96,14 @@ search.
|
|||
|
||||
The below tables describes the current attribute defaults for each implementation.
|
||||
|
||||
#### Search Base defaults
|
||||
|
||||
The following set defaults for the `additional_users_dn` and `additional_groups_dn` values.
|
||||
|
||||
| Implementation | Users | Groups |
|
||||
|:--------------:|:---------:|:---------:|
|
||||
| lldap | OU=people | OU=groups |
|
||||
|
||||
#### Attribute defaults
|
||||
|
||||
This table describes the attribute defaults for each implementation. i.e. the username_attribute is described by the
|
||||
|
@ -92,6 +114,7 @@ Username column.
|
|||
| custom | N/A | displayName | mail | cn |
|
||||
| activedirectory | sAMAccountName | displayName | mail | cn |
|
||||
| freeipa | uid | displayName | mail | cn |
|
||||
| lldap | uid | cn | mail | cn |
|
||||
|
||||
#### Filter defaults
|
||||
|
||||
|
@ -113,6 +136,7 @@ the following conditions:
|
|||
| 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)) |
|
||||
|
||||
##### Microsoft Active Directory sAMAccountType
|
||||
|
||||
|
|
|
@ -286,6 +286,7 @@ authentication_backend:
|
|||
## Acceptable options are as follows:
|
||||
## - 'activedirectory' - for Microsoft Active Directory.
|
||||
## - 'freeipa' - for FreeIPA.
|
||||
## - 'lldap' - for lldap.
|
||||
## - 'custom' - for custom specifications of attributes and filters.
|
||||
## This currently defaults to 'custom' to maintain existing behaviour.
|
||||
##
|
||||
|
|
|
@ -212,3 +212,19 @@ var DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA = LDAPAut
|
|||
MinimumVersion: TLSVersion{tls.VersionTLS12},
|
||||
},
|
||||
}
|
||||
|
||||
// DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP represents the default LDAP config for the LDAPImplementationLLDAP Implementation.
|
||||
var DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP = LDAPAuthenticationBackend{
|
||||
AdditionalUsersDN: "OU=people",
|
||||
AdditionalGroupsDN: "OU=groups",
|
||||
UsersFilter: "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person))",
|
||||
UsernameAttribute: ldapAttrUserID,
|
||||
MailAttribute: ldapAttrMail,
|
||||
DisplayNameAttribute: ldapAttrCommonName,
|
||||
GroupsFilter: "(&(member={dn})(objectClass=groupOfUniqueNames))",
|
||||
GroupNameAttribute: ldapAttrCommonName,
|
||||
Timeout: time.Second * 5,
|
||||
TLS: &TLSConfig{
|
||||
MinimumVersion: TLSVersion{tls.VersionTLS12},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -67,6 +67,9 @@ const (
|
|||
|
||||
// LDAPImplementationFreeIPA is the string for the FreeIPA LDAP implementation.
|
||||
LDAPImplementationFreeIPA = "freeipa"
|
||||
|
||||
// LDAPImplementationLLDAP is the string for the lldap LDAP implementation.
|
||||
LDAPImplementationLLDAP = "lldap"
|
||||
)
|
||||
|
||||
// TOTP Algorithm.
|
||||
|
|
|
@ -330,6 +330,8 @@ func validateLDAPAuthenticationBackend(config *schema.AuthenticationBackend, val
|
|||
implementation = &schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory
|
||||
case schema.LDAPImplementationFreeIPA:
|
||||
implementation = &schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA
|
||||
case schema.LDAPImplementationLLDAP:
|
||||
implementation = &schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP
|
||||
default:
|
||||
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendImplementation, config.LDAP.Implementation, strings.Join(validLDAPImplementations, "', '")))
|
||||
}
|
||||
|
@ -383,6 +385,14 @@ func ldapImplementationShouldSetStr(config, implementation string) bool {
|
|||
}
|
||||
|
||||
func setDefaultImplementationLDAPAuthenticationBackendProfileAttributes(config *schema.LDAPAuthenticationBackend, implementation *schema.LDAPAuthenticationBackend) {
|
||||
if ldapImplementationShouldSetStr(config.AdditionalUsersDN, implementation.AdditionalUsersDN) {
|
||||
config.AdditionalUsersDN = implementation.AdditionalUsersDN
|
||||
}
|
||||
|
||||
if ldapImplementationShouldSetStr(config.AdditionalGroupsDN, implementation.AdditionalGroupsDN) {
|
||||
config.AdditionalGroupsDN = implementation.AdditionalGroupsDN
|
||||
}
|
||||
|
||||
if ldapImplementationShouldSetStr(config.UsersFilter, implementation.UsersFilter) {
|
||||
config.UsersFilter = implementation.UsersFilter
|
||||
}
|
||||
|
|
|
@ -609,7 +609,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenImplementat
|
|||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'implementation' is configured as 'masd' but must be one of the following values: 'custom', 'activedirectory', 'freeipa'")
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'implementation' is configured as 'masd' but must be one of the following values: 'custom', 'activedirectory', 'freeipa', 'lldap'")
|
||||
}
|
||||
|
||||
func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenURLNotProvided() {
|
||||
|
@ -906,6 +906,12 @@ func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldSetActiveDirec
|
|||
suite.Assert().Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.Timeout,
|
||||
suite.config.LDAP.Timeout)
|
||||
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)
|
||||
|
@ -934,12 +940,20 @@ func (suite *ActiveDirectoryAuthenticationBackendSuite) TestShouldOnlySetDefault
|
|||
suite.config.LDAP.DisplayNameAttribute = "name"
|
||||
suite.config.LDAP.GroupsFilter = "(&(member={dn})(objectClass=group)(objectCategory=group))"
|
||||
suite.config.LDAP.GroupNameAttribute = "distinguishedName"
|
||||
suite.config.LDAP.AdditionalUsersDN = "OU=test"
|
||||
suite.config.LDAP.AdditionalGroupsDN = "OU=grps"
|
||||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.Timeout,
|
||||
suite.config.LDAP.Timeout)
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.AdditionalUsersDN,
|
||||
suite.config.LDAP.AdditionalUsersDN)
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.AdditionalGroupsDN,
|
||||
suite.config.LDAP.AdditionalGroupsDN)
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory.UsersFilter,
|
||||
suite.config.LDAP.UsersFilter)
|
||||
|
@ -1009,6 +1023,12 @@ func (suite *FreeIPAAuthenticationBackendSuite) TestShouldSetDefaults() {
|
|||
suite.Assert().Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.Timeout,
|
||||
suite.config.LDAP.Timeout)
|
||||
suite.Assert().Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.AdditionalUsersDN,
|
||||
suite.config.LDAP.AdditionalUsersDN)
|
||||
suite.Assert().Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.AdditionalGroupsDN,
|
||||
suite.config.LDAP.AdditionalGroupsDN)
|
||||
suite.Assert().Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.UsersFilter,
|
||||
suite.config.LDAP.UsersFilter)
|
||||
|
@ -1037,12 +1057,20 @@ func (suite *FreeIPAAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotMa
|
|||
suite.config.LDAP.DisplayNameAttribute = "gecos"
|
||||
suite.config.LDAP.GroupsFilter = "(&(member={dn})(objectClass=posixgroup))"
|
||||
suite.config.LDAP.GroupNameAttribute = "groupName"
|
||||
suite.config.LDAP.AdditionalUsersDN = "OU=people"
|
||||
suite.config.LDAP.AdditionalGroupsDN = "OU=grp"
|
||||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.Timeout,
|
||||
suite.config.LDAP.Timeout)
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.AdditionalUsersDN,
|
||||
suite.config.LDAP.AdditionalUsersDN)
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.AdditionalGroupsDN,
|
||||
suite.config.LDAP.AdditionalGroupsDN)
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationFreeIPA.UsersFilter,
|
||||
suite.config.LDAP.UsersFilter)
|
||||
|
@ -1066,3 +1094,105 @@ func (suite *FreeIPAAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotMa
|
|||
func TestFreeIPAAuthenticationBackend(t *testing.T) {
|
||||
suite.Run(t, new(FreeIPAAuthenticationBackendSuite))
|
||||
}
|
||||
|
||||
type LLDAPAuthenticationBackendSuite struct {
|
||||
suite.Suite
|
||||
config schema.AuthenticationBackend
|
||||
validator *schema.StructValidator
|
||||
}
|
||||
|
||||
func (suite *LLDAPAuthenticationBackendSuite) SetupTest() {
|
||||
suite.validator = schema.NewStructValidator()
|
||||
suite.config = schema.AuthenticationBackend{}
|
||||
suite.config.LDAP = &schema.LDAPAuthenticationBackend{}
|
||||
suite.config.LDAP.Implementation = schema.LDAPImplementationLLDAP
|
||||
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.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.TLS
|
||||
}
|
||||
|
||||
func (suite *LLDAPAuthenticationBackendSuite) TestShouldSetDefaults() {
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Assert().Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.Timeout,
|
||||
suite.config.LDAP.Timeout)
|
||||
suite.Assert().Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.AdditionalUsersDN,
|
||||
suite.config.LDAP.AdditionalUsersDN)
|
||||
suite.Assert().Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.AdditionalGroupsDN,
|
||||
suite.config.LDAP.AdditionalGroupsDN)
|
||||
suite.Assert().Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.UsersFilter,
|
||||
suite.config.LDAP.UsersFilter)
|
||||
suite.Assert().Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.UsernameAttribute,
|
||||
suite.config.LDAP.UsernameAttribute)
|
||||
suite.Assert().Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.DisplayNameAttribute,
|
||||
suite.config.LDAP.DisplayNameAttribute)
|
||||
suite.Assert().Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.MailAttribute,
|
||||
suite.config.LDAP.MailAttribute)
|
||||
suite.Assert().Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.GroupsFilter,
|
||||
suite.config.LDAP.GroupsFilter)
|
||||
suite.Assert().Equal(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.GroupNameAttribute,
|
||||
suite.config.LDAP.GroupNameAttribute)
|
||||
}
|
||||
|
||||
func (suite *LLDAPAuthenticationBackendSuite) TestShouldOnlySetDefaultsIfNotManuallyConfigured() {
|
||||
suite.config.LDAP.Timeout = time.Second * 2
|
||||
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.AdditionalUsersDN = "OU=no"
|
||||
suite.config.LDAP.AdditionalGroupsDN = "OU=yes"
|
||||
|
||||
ValidateAuthenticationBackend(&suite.config, suite.validator)
|
||||
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.Timeout,
|
||||
suite.config.LDAP.Timeout)
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.AdditionalUsersDN,
|
||||
suite.config.LDAP.AdditionalUsersDN)
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.AdditionalGroupsDN,
|
||||
suite.config.LDAP.AdditionalGroupsDN)
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.Timeout,
|
||||
suite.config.LDAP.Timeout)
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.UsersFilter,
|
||||
suite.config.LDAP.UsersFilter)
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.UsernameAttribute,
|
||||
suite.config.LDAP.UsernameAttribute)
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.DisplayNameAttribute,
|
||||
suite.config.LDAP.DisplayNameAttribute)
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.MailAttribute,
|
||||
suite.config.LDAP.MailAttribute)
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.GroupsFilter,
|
||||
suite.config.LDAP.GroupsFilter)
|
||||
suite.Assert().NotEqual(
|
||||
schema.DefaultLDAPAuthenticationBackendConfigurationImplementationLLDAP.GroupNameAttribute,
|
||||
suite.config.LDAP.GroupNameAttribute)
|
||||
}
|
||||
|
||||
func TestLLDAPAuthenticationBackend(t *testing.T) {
|
||||
suite.Run(t, new(LLDAPAuthenticationBackendSuite))
|
||||
}
|
||||
|
|
|
@ -323,7 +323,7 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
validLDAPImplementations = []string{schema.LDAPImplementationCustom, schema.LDAPImplementationActiveDirectory, schema.LDAPImplementationFreeIPA}
|
||||
validLDAPImplementations = []string{schema.LDAPImplementationCustom, schema.LDAPImplementationActiveDirectory, schema.LDAPImplementationFreeIPA, schema.LDAPImplementationLLDAP}
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
Loading…
Reference in New Issue