Difference between revisions of "LDAP"

From RackTables Wiki
Jump to navigation Jump to search
(→‎direct access to LDAP: describe the "port" parameter)
 
(20 intermediate revisions by 4 users not shown)
Line 3: Line 3:
  
 
== trusting httpd ==
 
== trusting httpd ==
 +
in secret.php
 
<pre>
 
<pre>
 
$user_auth_src = 'httpd';
 
$user_auth_src = 'httpd';
Line 9: Line 10:
  
 
== direct access to LDAP ==
 
== direct access to LDAP ==
The following settings configure direct communication with LDAP server:
+
The following settings configure direct communication with LDAP server. '''Note that if your LDAP configuration doesn't work, you can revert to local passwords by setting <tt>$user_auth_src = 'database'</tt>.'''
 +
in secret.php
 
<pre>
 
<pre>
 
$user_auth_src = 'ldap';
 
$user_auth_src = 'ldap';
 
$require_local_account = FALSE;
 
$require_local_account = FALSE;
 
</pre>
 
</pre>
Further configuration is done through $LDAP_options array. Its contents varies in each particular environment depending on the type of LDAP server software, its schema and configuration. RackTables has been reported to work with OpenLDAP, ActiveDirectory and eDirectory servers. Meaning of each LDAP option is explained below. '''The table is accurate as of RackTables release 0.19.5.'''
+
The first setting makes RackTables execute the configuration defined in <tt>$LDAP_options</tt>. The second option makes RackTables accept any valid account existing in LDAP. Historically a user is required to be on the list of local accounts regardless of how his or her password is verified. For most LDAP environments this restriction makes no sense.
 +
 
 +
Further configuration is done through $LDAP_options array. Its content varies in each particular environment depending on the type of LDAP server software, its schema and configuration. RackTables has been reported to work with OpenLDAP, ActiveDirectory and eDirectory servers. Meaning of each LDAP option is explained below. '''The table is accurate as of RackTables release 0.20.8.'''
 
{| border=1
 
{| border=1
 
|-
 
|-
Line 23: Line 27:
 
| <tt>server</tt>
 
| <tt>server</tt>
 
| <span style="background-color:red;">yes</span>
 
| <span style="background-color:red;">yes</span>
| Hostname or IP address of LDAP server(s). When multiple servers are listed (delimited with spaces), they are used in the order of appearance until a successful connection is made.
+
| In its simplest form, this parameter is a string with the hostname (or IP address) of the LDAP server (<tt>'ldap.example.com'</tt>). A fault-tolerant configuration may make use of multiple LDAP servers, in which case the hostnames must be delimited with spaces (<tt>'ldap1.example.com ldap2.example.com ldap3.example.com'</tt>). The requests will be dispatched by PHP LDAP extension as follows:
 +
<blockquote>Note that hostname can be a space-separated list of LDAP host names. This is very useful for failover; if the first LDAP host is down, ldap_connect will ask the second LDAP host and so on.</blockquote>
 +
 
 +
Connections to LDAP servers are by default made to TCP port 389 using '''unencrypted''' version of the protocol. To protect '''sensitive information''' (usernames and passwords) on its way from WWW server to LDAP server, it is possible to switch to encrypted version of the protocol, LDAPS, which is typically available on TCP port 636. This is done with prepending <tt>"ldaps://"</tt> to respective hostname(s): <tt>'ldaps://ldap.example.com'</tt> and <tt>'ldaps://ldap1.example.com ldaps://ldap2.example.com ldaps://ldap3.example.com'</tt>. Beware that LDAPS protocol requires appropriate setup of each configured server, including a valid SSL certificate, a key and a CA chain.
 +
 
 +
A more complete description of connection handling performed by PHP LDAP extension can be found in [http://php.net/ldap_connect ldap_connect function documentation].
 
|-
 
|-
| <tt>cache_refresh</tt> <tt>cache_retry</tt> <tt>cache_expiry</tt>
+
| <tt>port</tt>
| <span style="background-color:red;">yes</span>
+
| <span style="background-color:green;">no</span>
| LDAP cache parameters, values in seconds. Refresh, retry and expiry values are treated exactly as those for DNS SOA record. Example values 300-15-600 mean: unconditionally remeber successful auth for 5 minutes, after that still permit user access, but try to revalidate the password on the server (not more often, than once in 15 seconds). After 10 minutes of unsuccessful retries give up and deny access until the LDAP server gets fixed.
+
| This optional parameter sets the default TCP port to use when an entry in the <tt>server</tt> parameter above does not specify a port number explicitly. When an entry does specify a port number, it overrides this parameter.
Like in DNS, the following condition must be always met: cache_retry <= cache_refresh <= cache_expiry
 
To disable LDAP cache completely, set cache_refresh, cache_retry and cache_expiry to 0.
 
 
|-
 
|-
 
| <tt>domain</tt>
 
| <tt>domain</tt>
 
| <span style="background-color:yellow;">varies</span>
 
| <span style="background-color:yellow;">varies</span>
| When this option is set, it is used for "domain" authentication mode specific to ActiveDirectory. In this mode the plain username presented by a user is joined with the configured domain name and the resulting string ("username@example.com") is used for password validation. '''Either "domain" or "search" mode must be enabled.'''
+
| When this option is set, it is used for "domain" authentication mode specific to ActiveDirectory. In this mode the plain username presented by a user is joined with the configured domain name and the resulting string ("username@example.com") is used for password validation. '''At least one of "domain" and "search" modes must be enabled.'''
 
|-
 
|-
 
| <tt>search_attr</tt> <tt>search_dn</tt>
 
| <tt>search_attr</tt> <tt>search_dn</tt>
 
| <span style="background-color:yellow;">varies</span>
 
| <span style="background-color:yellow;">varies</span>
| These options enable "search" authentication mode, which is more common for OpenLDAP and eDirectory servers. It is assumed, that many nested organizational units (OUs) exist within within an organization (O). Traditional LDAP implementations let each OU have its own list of usernames, and some usernames remain unique only within their OU. The following approach is used to distinguish the users. Users with globally unique usernames present their plain username. Other users know, that they have to use their UID instead. A LDAP database is maintaned so, that UID is a little longer, than username, but yet unique. To map presented UID into common name (CN, which is the canonical, long form of user's identifier) LDAP search is performed. All this works automatically, once the search DN and search attribute are configured. '''Either "domain" or "search" mode must be enabled.'''
+
| These options enable "search" authentication mode, which is more common for OpenLDAP and eDirectory servers. It is assumed, that many nested organizational units (OUs) exist within within an organization (O). Traditional LDAP implementations let each OU have its own list of usernames, and some usernames remain unique only within their OU. The following approach is used to distinguish the users. Users with globally unique usernames present their plain username. Other users know, that they have to use their UID instead. A LDAP database is maintaned so, that UID is a little longer, than username, but yet unique. To map presented UID into common name (CN, which is the canonical, long form of user's identifier) LDAP search is performed. All this works automatically, once the search DN and search attribute are configured. '''At least one of "domain" and "search" modes must be enabled.'''
 +
|-
 +
| <tt>search_bind_rdn</tt> <tt>search_bind_password</tt>
 +
| <span style="background-color:green;">no</span>
 +
| If provided, racktables will bind using these credentials before searching for a username. Otherwise an anonymous binding is used. '''This setting only works in "search" mode. This option was added in release 0.19.12.'''
 
|-
 
|-
 
| <tt>displayname_attrs</tt>
 
| <tt>displayname_attrs</tt>
Line 45: Line 56:
 
| <tt>group_attr</tt>
 
| <tt>group_attr</tt>
 
| <span style="background-color:green;">no</span> (has default)
 
| <span style="background-color:green;">no</span> (has default)
| Group membership attribute, default value is "memberof". When it is configured, it is used as the source of authenticated user's LDAP groups. Upon successful authentication the groups are mapped into a set of autotags in the form {$lgcn_LDAPGroup1}, {$lgcn_LDAPGroup2}, {$lgcn_LDAPGroup3}... '''This setting only works in "search" mode.'''
+
| Group membership attribute, default value is "memberof". When it is configured, it is used as the source of authenticated user's LDAP groups. Upon successful authentication the groups are mapped into a set of autotags in the form {$lgcn_LDAPGroup1}, {$lgcn_LDAPGroup2}, {$lgcn_LDAPGroup3}... '''This setting only works in "search" mode.''' "lgcn" stands for "LDAP Group Common Name", because only the CN part of each LDAP group is used to generate an autotag.
 
|-
 
|-
 
| <tt>group_filter</tt>
 
| <tt>group_filter</tt>
 
| <span style="background-color:green;">no</span> (has default)
 
| <span style="background-color:green;">no</span> (has default)
| User groups filter regexp (PCRE). The purpose of this filter is to decide, which LDAP groups are mapped to a {$lgcn_XXXXXX} autotag and which are not. Group DNs matching the regexp are mapped and the other groups are not. The default value is <tt>"/^[Cc][Nn]=([^,]+)/"</tt>, which matches any valid LDAP group. In order to generate the autotags only for groups within a given OU, the following PCRE could be used: <tt>"/CN=([^,]+),OU=RackTables,OU=IT,O=company/"</tt>
+
| User groups filter regexp (PCRE). The purpose of this filter is to decide, which LDAP groups are mapped to a {$lgcn_XXXXXX} autotag and which are not. Group DNs matching the regexp are mapped and the other groups are not. The default value is <tt>"/^[Cc][Nn]=([^,]+)/"</tt>, which matches any valid LDAP group. In order to generate the autotags only for groups within a given OU, the following PCRE could be used: <tt>"/CN=([^,]+),OU=RackTables,OU=IT,O=company/"</tt> '''This setting only works in "search" mode.'''
 +
|-
 +
| <tt>cache_refresh</tt> <tt>cache_retry</tt> <tt>cache_expiry</tt>
 +
| <span style="background-color:green;">no</span> (has default)
 +
| LDAP cache parameters, values in seconds. Refresh, retry and expiry values are treated exactly as those for DNS SOA record. Default values 300-15-600 mean: unconditionally remeber successful auth for 5 minutes, after that still permit user access, but try to revalidate the password on the server (not more often, than once in 15 seconds). After 10 minutes of unsuccessful retries give up and deny access until the LDAP server gets fixed.
 +
Like in DNS, the following condition must be always met: cache_retry <= cache_refresh <= cache_expiry
 +
To disable LDAP cache completely, set cache_refresh, cache_retry and cache_expiry to 0.
 
|-
 
|-
 
| <tt>options</tt>
 
| <tt>options</tt>
 
| <span style="background-color:green;">no</span>
 
| <span style="background-color:green;">no</span>
| List of LDAP protocol options as explained at http://php.net/manual/en/function.ldap-set-option.php PHP LDAP extension often uses LDAP protocol version 2 by default, which is deprecated. All examples below include a setting to use LDAP protocol version 3.
+
| List of LDAP protocol options as explained at http://php.net/manual/en/function.ldap-set-option.php (see the note on LDAP protocol version below).
 +
|-
 +
| <tt>use_tls</tt>
 +
| <span style="background-color:green;">no</span>
 +
|
 +
* If it is unset or set to 0, then TLS negotiation is disabled completely.
 +
* If it's set to 1, then TLS negotiation is attempted.
 +
* If it's set to 2, then TLS is mandatory and if the negotiation fails, it throws an exception. A TLS negotiation failure generally indicates a serious security policy failure + configuration problem, so it's probably better to make noise about a problem like this rather than pretending nothing happened and that this was a minor issue. It also makes debugging much easier.
 +
 
 +
'''LDAP TLS requires LDAPv3. This option was added in release 0.19.11.'''
 
|}
 
|}
 +
 +
=== a note on the "domain+search" mode ===
 +
It is possible to have both "domain" and "search" modes enabled. In this case "domain" mode will be used for password validation and "search" mode will be used to retrieve <tt>displayname_attrs</tt> and <tt>group_attr</tt> data. The current limitation is that <tt>group_attr</tt> is not processed without <tt>displayname_attrs</tt> configured, this is probably a bug.
 +
 +
=== a note on LDAP protocol versions ===
 +
Most modern LDAP servers are configured to allow only version 3 LDAP clients, even if the server has support for version 2 (which is considered legacy). Although PHP LDAP extension supports both version 2 and version 3 of the protocol, it often defaults to version 2 unless explicitly requested to use version 3. This breaks an otherwise properly configured RackTables instance.
 +
 +
Craig Hoffman notes a way of troubleshooting the version mismatch with an OpenLDAP server. The server should be temporarily run in the debug mode:
 +
<pre>
 +
# slapd -d 16380
 +
</pre>
 +
Then each attempt to authenticate in RackTables would result in the following messages in the server log:
 +
<pre>
 +
send_ldap_result: err=2 matched="" text="historical protocol version requested, use LDAPv3 instead"
 +
conn=2 op=1 RESULT tag=97 err=2 text=historical protocol version requested, use LDAPv3 instead
 +
</pre>
 +
The '''recommended solution''' is to configure RackTables to use LDAP version 3. All configuration examples below are written with this setting in mind.
 +
 +
A last-resort workaround is to reconfigure the LDAP server to allow version 2, in the case of OpenLDAP it would be:
 +
<pre>
 +
# You have to place it before the database is instantiated, for example,
 +
# at the very top of slapd.conf
 +
#
 +
# Global Directives:
 +
allow bind_v2
 +
</pre>
  
 
=== configuring for OpenLDAP in "search" mode ===
 
=== configuring for OpenLDAP in "search" mode ===
Line 61: Line 113:
 
(
 
(
 
   'server' => 'ldap.example.com',
 
   'server' => 'ldap.example.com',
  'cache_refresh' => 300,
 
  'cache_retry' => 15,
 
  'cache_expiry' => 600,
 
 
   'search_attr' => 'uid',
 
   'search_attr' => 'uid',
 
   'search_dn' => 'OU=people,O=YourCompany',
 
   'search_dn' => 'OU=people,O=YourCompany',
Line 75: Line 124:
 
(
 
(
 
   'server' => 'ldap.example.com',
 
   'server' => 'ldap.example.com',
  'cache_refresh' => 300,
 
  'cache_retry' => 15,
 
  'cache_expiry' => 600,
 
 
   'search_attr' => 'uid',
 
   'search_attr' => 'uid',
 
   'search_dn' => 'OU=people,O=YourCompany',
 
   'search_dn' => 'OU=people,O=YourCompany',
Line 85: Line 131:
 
);
 
);
 
</pre>
 
</pre>
=== configuring for ActiveDirectory in "domain" mode ===
+
=== configuring for ActiveDirectory in "domain+search" mode ===
 
Note the additional protocol option, which disables referrals to make LDAP search happen in the base DN, but not in nested OUs/CNs. This is important for some ActiveDirectory servers.
 
Note the additional protocol option, which disables referrals to make LDAP search happen in the base DN, but not in nested OUs/CNs. This is important for some ActiveDirectory servers.
 
<pre>
 
<pre>
 
$LDAP_options = array
 
$LDAP_options = array
 
(
 
(
   'server' => 'ldap.example.com',
+
   'server' => 'domaincontroller.example.com',
  'cache_refresh' => 300,
 
  'cache_retry' => 15,
 
  'cache_expiry' => 600,
 
 
   'domain' => 'example.com',
 
   'domain' => 'example.com',
 +
  'search_attr' => 'sAMAccountName',
 +
  'search_dn' => 'OU=Users,DC=example,DC=com',
 
   'displayname_attrs' => 'givenname sn',
 
   'displayname_attrs' => 'givenname sn',
 
   'options' => array (LDAP_OPT_PROTOCOL_VERSION => 3, LDAP_OPT_REFERRALS => 0),
 
   'options' => array (LDAP_OPT_PROTOCOL_VERSION => 3, LDAP_OPT_REFERRALS => 0),
 
);
 
);
 
</pre>
 
</pre>

Latest revision as of 11:56, 4 March 2016

There are different ways to make RackTables recognize LDAP accounts instead of local accounts. One way is to configure RackTables to communicate with an LDAP server directly. In this case both password validity and group membership information are available inside RackTables. Group membership can be used in permission rules to implement site's access control policy. Another way is to configure RackTables to trust the authentication already performed by httpd, which in turn is configured to authenticate HTTP(S) clients in LDAP. This is sometimes the case, when the system administrator wants to reuse a working httpd+mod_auth_ldap setup. The drawback of this method is that group membership information wouldn't be available at RackTables level.

trusting httpd

in secret.php

$user_auth_src = 'httpd';
$require_local_account = FALSE;

direct access to LDAP

The following settings configure direct communication with LDAP server. Note that if your LDAP configuration doesn't work, you can revert to local passwords by setting $user_auth_src = 'database'. in secret.php

$user_auth_src = 'ldap';
$require_local_account = FALSE;

The first setting makes RackTables execute the configuration defined in $LDAP_options. The second option makes RackTables accept any valid account existing in LDAP. Historically a user is required to be on the list of local accounts regardless of how his or her password is verified. For most LDAP environments this restriction makes no sense.

Further configuration is done through $LDAP_options array. Its content varies in each particular environment depending on the type of LDAP server software, its schema and configuration. RackTables has been reported to work with OpenLDAP, ActiveDirectory and eDirectory servers. Meaning of each LDAP option is explained below. The table is accurate as of RackTables release 0.20.8.

option(s) is mandatory? description
server yes In its simplest form, this parameter is a string with the hostname (or IP address) of the LDAP server ('ldap.example.com'). A fault-tolerant configuration may make use of multiple LDAP servers, in which case the hostnames must be delimited with spaces ('ldap1.example.com ldap2.example.com ldap3.example.com'). The requests will be dispatched by PHP LDAP extension as follows:

Note that hostname can be a space-separated list of LDAP host names. This is very useful for failover; if the first LDAP host is down, ldap_connect will ask the second LDAP host and so on.

Connections to LDAP servers are by default made to TCP port 389 using unencrypted version of the protocol. To protect sensitive information (usernames and passwords) on its way from WWW server to LDAP server, it is possible to switch to encrypted version of the protocol, LDAPS, which is typically available on TCP port 636. This is done with prepending "ldaps://" to respective hostname(s): 'ldaps://ldap.example.com' and 'ldaps://ldap1.example.com ldaps://ldap2.example.com ldaps://ldap3.example.com'. Beware that LDAPS protocol requires appropriate setup of each configured server, including a valid SSL certificate, a key and a CA chain.

A more complete description of connection handling performed by PHP LDAP extension can be found in ldap_connect function documentation.

port no This optional parameter sets the default TCP port to use when an entry in the server parameter above does not specify a port number explicitly. When an entry does specify a port number, it overrides this parameter.
domain varies When this option is set, it is used for "domain" authentication mode specific to ActiveDirectory. In this mode the plain username presented by a user is joined with the configured domain name and the resulting string ("username@example.com") is used for password validation. At least one of "domain" and "search" modes must be enabled.
search_attr search_dn varies These options enable "search" authentication mode, which is more common for OpenLDAP and eDirectory servers. It is assumed, that many nested organizational units (OUs) exist within within an organization (O). Traditional LDAP implementations let each OU have its own list of usernames, and some usernames remain unique only within their OU. The following approach is used to distinguish the users. Users with globally unique usernames present their plain username. Other users know, that they have to use their UID instead. A LDAP database is maintaned so, that UID is a little longer, than username, but yet unique. To map presented UID into common name (CN, which is the canonical, long form of user's identifier) LDAP search is performed. All this works automatically, once the search DN and search attribute are configured. At least one of "domain" and "search" modes must be enabled.
search_bind_rdn search_bind_password no If provided, racktables will bind using these credentials before searching for a username. Otherwise an anonymous binding is used. This setting only works in "search" mode. This option was added in release 0.19.12.
displayname_attrs no This list of space-delimited attributes is used to build the "displayed name" of a user, which is used to print a greeting message. This setting only works in "search" mode.
group_attr no (has default) Group membership attribute, default value is "memberof". When it is configured, it is used as the source of authenticated user's LDAP groups. Upon successful authentication the groups are mapped into a set of autotags in the form {$lgcn_LDAPGroup1}, {$lgcn_LDAPGroup2}, {$lgcn_LDAPGroup3}... This setting only works in "search" mode. "lgcn" stands for "LDAP Group Common Name", because only the CN part of each LDAP group is used to generate an autotag.
group_filter no (has default) User groups filter regexp (PCRE). The purpose of this filter is to decide, which LDAP groups are mapped to a {$lgcn_XXXXXX} autotag and which are not. Group DNs matching the regexp are mapped and the other groups are not. The default value is "/^[Cc][Nn]=([^,]+)/", which matches any valid LDAP group. In order to generate the autotags only for groups within a given OU, the following PCRE could be used: "/CN=([^,]+),OU=RackTables,OU=IT,O=company/" This setting only works in "search" mode.
cache_refresh cache_retry cache_expiry no (has default) LDAP cache parameters, values in seconds. Refresh, retry and expiry values are treated exactly as those for DNS SOA record. Default values 300-15-600 mean: unconditionally remeber successful auth for 5 minutes, after that still permit user access, but try to revalidate the password on the server (not more often, than once in 15 seconds). After 10 minutes of unsuccessful retries give up and deny access until the LDAP server gets fixed.

Like in DNS, the following condition must be always met: cache_retry <= cache_refresh <= cache_expiry To disable LDAP cache completely, set cache_refresh, cache_retry and cache_expiry to 0.

options no List of LDAP protocol options as explained at http://php.net/manual/en/function.ldap-set-option.php (see the note on LDAP protocol version below).
use_tls no
  • If it is unset or set to 0, then TLS negotiation is disabled completely.
  • If it's set to 1, then TLS negotiation is attempted.
  • If it's set to 2, then TLS is mandatory and if the negotiation fails, it throws an exception. A TLS negotiation failure generally indicates a serious security policy failure + configuration problem, so it's probably better to make noise about a problem like this rather than pretending nothing happened and that this was a minor issue. It also makes debugging much easier.

LDAP TLS requires LDAPv3. This option was added in release 0.19.11.

a note on the "domain+search" mode

It is possible to have both "domain" and "search" modes enabled. In this case "domain" mode will be used for password validation and "search" mode will be used to retrieve displayname_attrs and group_attr data. The current limitation is that group_attr is not processed without displayname_attrs configured, this is probably a bug.

a note on LDAP protocol versions

Most modern LDAP servers are configured to allow only version 3 LDAP clients, even if the server has support for version 2 (which is considered legacy). Although PHP LDAP extension supports both version 2 and version 3 of the protocol, it often defaults to version 2 unless explicitly requested to use version 3. This breaks an otherwise properly configured RackTables instance.

Craig Hoffman notes a way of troubleshooting the version mismatch with an OpenLDAP server. The server should be temporarily run in the debug mode:

# slapd -d 16380

Then each attempt to authenticate in RackTables would result in the following messages in the server log:

send_ldap_result: err=2 matched="" text="historical protocol version requested, use LDAPv3 instead"
conn=2 op=1 RESULT tag=97 err=2 text=historical protocol version requested, use LDAPv3 instead

The recommended solution is to configure RackTables to use LDAP version 3. All configuration examples below are written with this setting in mind.

A last-resort workaround is to reconfigure the LDAP server to allow version 2, in the case of OpenLDAP it would be:

# You have to place it before the database is instantiated, for example,
# at the very top of slapd.conf
#
# Global Directives:
allow bind_v2

configuring for OpenLDAP in "search" mode

$LDAP_options = array
(
  'server' => 'ldap.example.com',
  'search_attr' => 'uid',
  'search_dn' => 'OU=people,O=YourCompany',
  'displayname_attrs' => 'givenname familyname',
  'options' => array (LDAP_OPT_PROTOCOL_VERSION => 3),
);

configuring for eDirectory in "search" mode

$LDAP_options = array
(
  'server' => 'ldap.example.com',
  'search_attr' => 'uid',
  'search_dn' => 'OU=people,O=YourCompany',
  'displayname_attrs' => 'givenname familyname',
  'group_attr' => 'groupmembership',
  'options' => array (LDAP_OPT_PROTOCOL_VERSION => 3),
);

configuring for ActiveDirectory in "domain+search" mode

Note the additional protocol option, which disables referrals to make LDAP search happen in the base DN, but not in nested OUs/CNs. This is important for some ActiveDirectory servers.

$LDAP_options = array
(
  'server' => 'domaincontroller.example.com',
  'domain' => 'example.com',
  'search_attr' => 'sAMAccountName',
  'search_dn' => 'OU=Users,DC=example,DC=com',
  'displayname_attrs' => 'givenname sn',
  'options' => array (LDAP_OPT_PROTOCOL_VERSION => 3, LDAP_OPT_REFERRALS => 0),
);