From: Arran Cudbard-Bell Date: Mon, 21 Oct 2019 02:18:30 +0000 (-0400) Subject: Break up LDAP howto pages into yet smaller pages X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7ffba591d0dbaeb800e42e273dd074fd0aae2718;p=thirdparty%2Ffreeradius-server.git Break up LDAP howto pages into yet smaller pages --- diff --git a/doc/antora/modules/concepts/pages/modules/ldap/authentication.adoc b/doc/antora/modules/concepts/pages/modules/ldap/authentication.adoc index bc35ac6eee6..9766d09d35f 100644 --- a/doc/antora/modules/concepts/pages/modules/ldap/authentication.adoc +++ b/doc/antora/modules/concepts/pages/modules/ldap/authentication.adoc @@ -94,7 +94,7 @@ involves giving the FreeRADIUS "read-only" user permission to read the `userPassword` field. Again, the best method is to test authentication is with the -xref:howto:modules/ldap/ldapsearch.adoc[ldapsearch] tool. +xref:howto:modules/ldap/ldapsearch/index.adoc[ldapsearch] tool. These tests *must* be run prior to configuring FreeRADIUS. We strongly recommend having the LDAP database return the `userPassword` field to FreeRADIUS, so that FreeRADIUS can authenticate the user. diff --git a/doc/antora/modules/howto/nav.adoc b/doc/antora/modules/howto/nav.adoc index 473b19d528d..51810822168 100644 --- a/doc/antora/modules/howto/nav.adoc +++ b/doc/antora/modules/howto/nav.adoc @@ -5,14 +5,24 @@ *** xref:modules/eap/index.adoc[EAP] *** xref:modules/expiration/index.adoc[Expiration] *** xref:modules/krb5/index.adoc[Krb5] + *** xref:modules/ldap/index.adoc[LDAP] -**** xref:modules/ldap/bootstrap_openldap.adoc[Installing and Configuring OpenLDAP] -**** xref:modules/ldap/ldapsearch.adoc[Mapping and testing with ldapsearch] + +**** xref:modules/ldap/bootstrap_openldap/index.adoc[Installing and Configuring OpenLDAP] +***** xref:modules/ldap/bootstrap_openldap/docker.adoc[Docker] +***** xref:modules/ldap/bootstrap_openldap/packages.adoc[Packages] + +**** xref:modules/ldap/ldapsearch/index.adoc[Mapping and testing with ldapsearch] +***** xref:modules/ldap/ldapsearch/before_starting.adoc[Before starting] +***** xref:modules/ldap/ldapsearch/connection_parameters.adoc[Determining connection parameters] +***** xref:modules/ldap/ldapsearch/locating_objects.adoc[Locating objects] +***** xref:modules/ldap/ldapsearch/translating_to_the_ldap_module.adoc[Translating ldapsearch arguments to LDAP configuration items] **** xref:modules/ldap/configuration.adoc[Enabling the LDAP module] **** xref:modules/ldap/authorization.adoc[Configuring authorization] **** xref:modules/ldap/authentication.adoc[Configuring authentication] **** xref:modules/ldap/accounting.adoc[Configuring accounting] + *** xref:modules/mschap/index.adoc[MS-CHAP] *** xref:modules/pam/index.adoc[PAM] *** xref:modules/passwd/index.adoc[Passwd] diff --git a/doc/antora/modules/howto/pages/modules/ldap/authorization.adoc b/doc/antora/modules/howto/pages/modules/ldap/authorization.adoc index 010b3a211c8..22222c8bff3 100644 --- a/doc/antora/modules/howto/pages/modules/ldap/authorization.adoc +++ b/doc/antora/modules/howto/pages/modules/ldap/authorization.adoc @@ -1,5 +1,45 @@ = Configuring Authorization -=== Group Membership + +The `authorize` method of the LDAP module is responsible for locating the +authenticating user's LDAP object. + +In addition to determining where the user is, the authorize method also +performs LDAP to FreeRADIUS attribute mappings, + +== 1. Locating the user + +No matter how the LDAP module is called (via its `authorize`, `authenticate`, +`accounting` methods or an `LDAP-Group` comparison) the first operation the +module performs it to populate `&control:LDAP-UserDN` with the location of +the authenticating user's object in LDAP. + +How the LDAP-UserDN value is discovered is determined by the contents of the +`user { ... }` section of the LDAP module instance. + +Below is an example of configuring a user section for OpenLDAP, with callouts +explaining how the different configuration items are used. See +xref:modules/ldap/ldapsearch/index.adoc[ldapsearch] for how to determine the +appropriate values for your directory. + +[source,unlang] +---- +user { + base_dn = "ou=people,${..base_dn}" <1> + filter = +} +---- + +== 2. Disambiguating users + +dn: olcOverlay=sssvlv,olcDatabase={1}mdb,cn=config +> objectClass: olcSssVlvConfig +> olcOverlay: sssvlv +> olcSssVlvMax: 10 +> olcSssVlvMaxKeys: 5 + +== 2. Configuring the enable/disable attribute + +== Group Membership Depending on the LDAP vendor you use, LDAP group membership is either handled using the LDAP attributes: diff --git a/doc/antora/modules/howto/pages/modules/ldap/bootstrap_openldap/docker.adoc b/doc/antora/modules/howto/pages/modules/ldap/bootstrap_openldap/docker.adoc new file mode 100644 index 00000000000..63e0ef6ee6e --- /dev/null +++ b/doc/antora/modules/howto/pages/modules/ldap/bootstrap_openldap/docker.adoc @@ -0,0 +1,132 @@ += Using OpenLDAP in a docker container + +== Bootstrap + +https://github.com/osixia[Osixia!] provides a +https://github.com/osixia/docker-openldap[fully functionally OpenLDAP container] +which can be instantiated using the docker invocation below. + +This docker invocation also sets up a readonly user, and loads the custom +FreeRADIUS schemas required for RADIUS to LDAP attribute mapping, dynamic client +definitions, and attribute profiles. + +=== Define site specific variables +Change the values here to match local paths and your site specific +configuration. + +[source,shell] +---- +# Where to store a temporary shallow clone of the FreeRADIUS source +# Or the path to an existing copy of the FreeRADIUS source +FREERADIUS_SRC="/tmp/freeradius-src" + +# Base DN +LDAP_BASE_DN="dc=example,dc=com" + +# Domain +LDAP_DOMAIN="example.com" + +# DN Suffix +LDAP_BASE_DN="dc=example,dc=com" + +# Password for the administrative user +LDAP_ADMIN_PASSWORD="secret" + +# Password for the read only user +LDAP_READONLY_PASSWORD="readonly" +---- + +=== Instantiate an OpenLDAP docker container + +[source,shell] +---- +if [ ! -d "${FREERADIUS_SRC}" ]; then + git clone --depth 1 https://github.com/FreeRADIUS/freeradius-server.git "${FREERADIUS_SRC}" +fi +docker run -it --rm -p 389:389 \ + --env LDAP_DOMAIN=${LDAP_DOMAIN} \ + --env LDAP_ADMIN_PASSWORD=${LDAP_ADMIN_PASSWORD} \ + --env LDAP_READONLY_USER=true \ + --env LDAP_READONLY_USER_PASSWORD=${LDAP_READONLY_PASSWORD} \ + --volume "${FREERADIUS_SRC}/doc/schemas/ldap/openldap/freeradius.schema:/container/service/slapd/assets/config/bootstrap/schema/mmc/radius.schema:ro" \ + --volume "${FREERADIUS_SRC}/doc/schemas/ldap/openldap/freeradius-clients.schema:/container/service/slapd/assets/config/bootstrap/schema/mmc/freeradius-clients.schema:ro" \ + osixia/openldap:1.2.5 --copy-service +---- + +== Populate with test data +Once docker is running, there should now be an LDAP server running on +`localhost`, it can now be populated with test data. + +For test data we will be using the object definitions from the LDAP +module's CIT (Continuous Integration Testing) script. + +These object definitions have been designed to exercise all features +of the FreeRADIUS LDAP module. + +[source,shell] +---- +sed -e '1,/^description:/ d' "${FREERADIUS_SRC}/src/tests/modules/ldap/example.com.ldif" \ + | ldapadd -H ldap://localhost -x -D cn=admin,${LDAP_BASE_DN} -w admin +---- + +There should be no errors, and the console with the running LDAP +server should show requests being handled. + +If the LDAP server server is stopped, as with `CTRL-C`, then the database +contents will be lost. All of the steps above will have to be re-done the next +time the server is started. + +In order to make the LDAP database persistent, see the +https://github.com/osixia/docker-openldap[osixia/openldap +instructions]. + +== Check it works + +As a final step you should verify that test data has been loaded correctly. +This can be done using the `ldapsearch` utility using the LDAP read only user. + +The command below will retrieve the entry for one of the test `radiusClient` +entries. You should see a single search result returned if everything worked +correctly. + +[source,shell] +---- +ldapsearch -LLL -H ldap://localhost -x -D cn=readonly, -w -b '(&(objectClass=radiusClient)(radiusClientShortname=client2))' +---- + +.Searching for a RADIUS Client +==== +[source,shell] +---- +ldapsearch -LLL -H ldap://localhost -x -D cn=readonly,dc=example,dc=com -w readonly -b dc=example,dc=com '(&(objectClass=radiusClient)(radiusClientShortname=client2))' +---- + +.Expected output +[source,ldiff] +---- +# extended LDIF +# +# LDAPv3 +# base with scope subtree +# filter: (&(objectClass=radiusClient)(radiusClientShortname=client2)) +# requesting: ALL +# + +# 2.2.2.2, clients, example.com +dn: radiusClientIdentifier=2.2.2.2,ou=clients,dc=example,dc=com +objectClass: radiusClient +radiusClientIdentifier: 2.2.2.2 +radiusClientSecret: 123secret +radiusClientShortname: client2 +radiusClientType: cisco +radiusClientRequireMa: TRUE +radiusClientComment: Another test client + +# search result +search: 2 +result: 0 Success + +# numResponses: 2 +# numEntries: 1 +---- +==== diff --git a/doc/antora/modules/howto/pages/modules/ldap/bootstrap_openldap/index.adoc b/doc/antora/modules/howto/pages/modules/ldap/bootstrap_openldap/index.adoc new file mode 100644 index 00000000000..3d28f0ea728 --- /dev/null +++ b/doc/antora/modules/howto/pages/modules/ldap/bootstrap_openldap/index.adoc @@ -0,0 +1,22 @@ += Installing and Configuring OpenLDAP + +This section of the howto describes two methods of creating an OpenLDAP instance for +testing purposes. + +== xref:modules/ldap/bootstrap_openldap/docker.adoc[Docker] + +If you're looking to create a temporary instance of OpenLDAP and spin it up as +quickly as possible, you should consider using the +https://docs.docker.com/install/[Docker containerisation service]. + +A pre-configured docker container allow you to create a self-contained OpenLDAP +instance with a minimum amount of effort. + +== xref:modules/ldap/bootstrap_openldap/packages.adoc[Packages] + +If you're looking to create a more permanent installation of OpenLDAP or +are not comfortable using docker, then you may wish to install OpenLDAP from packages. + +OpenLDAP is available pre-packaged for many distributions. We recommend +using the OpenLDAP LTB packages available under the +"Packaging and OpenLDAP extensions" heading https://ltb-project.org/documentation[here]. diff --git a/doc/antora/modules/howto/pages/modules/ldap/bootstrap_openldap.adoc b/doc/antora/modules/howto/pages/modules/ldap/bootstrap_openldap/packages.adoc similarity index 66% rename from doc/antora/modules/howto/pages/modules/ldap/bootstrap_openldap.adoc rename to doc/antora/modules/howto/pages/modules/ldap/bootstrap_openldap/packages.adoc index 54830265b5e..38a72feaeb0 100644 --- a/doc/antora/modules/howto/pages/modules/ldap/bootstrap_openldap.adoc +++ b/doc/antora/modules/howto/pages/modules/ldap/bootstrap_openldap/packages.adoc @@ -1,113 +1,6 @@ -= Installing and Configuring OpenLDAP += Installing OpenLDAP from packages -This page describes two methods (using <> and via -<>) of creating an OpenLDAP instance for -testing purposes. - -If you're looking to create a temporary instance of OpenLDAP and spin it up as -quickly as possible, you should consider using the -https://docs.docker.com/install/[Docker containerisation service]. - -A pre-configured docker container allow you to create a self-contained OpenLDAP -instance with a minimum amount of effort. - -If you're looking to create a more permanent installation of OpenLDAP or -are not comfortable using docker, then you may wish to install OpenLDAP from packages. - -OpenLDAP is available pre-packaged for many distributions. We recommend -using the OpenLDAP LTB packages available under the -"Packaging and OpenLDAP extensions" heading https://ltb-project.org/documentation[here]. - -[#bootstrap_with_docker] -== Using OpenLDAP in a docker container - -=== Bootstrap - -https://github.com/osixia[Osixia!] provides a -https://github.com/osixia/docker-openldap[fully functionally OpenLDAP container] -which can be instantiated using the docker invocation below. - -This docker invocation also sets up a readonly user, and loads the custom -FreeRADIUS schemas required for RADIUS to LDAP attribute mapping, dynamic client -definitions, and attribute profiles. - -==== Define site specific variables -Change the values here to match local paths and your site specific -configuration. - -[source,shell] ----- -# Where to store a temporary shallow clone of the FreeRADIUS source -# Or the path to an existing copy of the FreeRADIUS source -FREERADIUS_SRC="/tmp/freeradius-src" - -# Base DN -LDAP_BASE_DN="dc=example,dc=com" - -# Domain -LDAP_DOMAIN="example.com" - -# DN Suffix -LDAP_BASE_DN="dc=example,dc=com" - -# Password for the administrative user -LDAP_ADMIN_PASSWORD="secret" - -# Password for the read only user -LDAP_READONLY_PASSWORD="readonly" ----- - -==== Instantiate an OpenLDAP docker container - -[source,shell] ----- -if [ ! -d "${FREERADIUS_SRC}" ]; then - git clone --depth 1 https://github.com/FreeRADIUS/freeradius-server.git "${FREERADIUS_SRC}" -fi -docker run -it --rm -p 389:389 \ - --env LDAP_DOMAIN=${LDAP_DOMAIN} \ - --env LDAP_ADMIN_PASSWORD=${LDAP_ADMIN_PASSWORD} \ - --env LDAP_READONLY_USER=true \ - --env LDAP_READONLY_USER_PASSWORD=${LDAP_READONLY_PASSWORD} \ - --volume "${FREERADIUS_SRC}/doc/schemas/ldap/openldap/freeradius.schema:/container/service/slapd/assets/config/bootstrap/schema/mmc/radius.schema:ro" \ - --volume "${FREERADIUS_SRC}/doc/schemas/ldap/openldap/freeradius-clients.schema:/container/service/slapd/assets/config/bootstrap/schema/mmc/freeradius-clients.schema:ro" \ - osixia/openldap:1.2.5 --copy-service ----- - -=== Test data -Once docker is running, there should now be an LDAP server running on -`localhost`, it can now be populated with test data. - -For test data we will be using the object definitions from the LDAP -module's CIT (Continuous Integration Testing) script. - -These object definitions have been designed to exercise all features -of the FreeRADIUS LDAP module. - -[source,shell] ----- -sed -e '1,/^description:/ d' "${FREERADIUS_SRC}/src/tests/modules/ldap/example.com.ldif" \ - | ldapadd -H ldap://localhost -x -D cn=admin,${LDAP_BASE_DN} -w admin ----- - -There should be no errors, and the console with the running LDAP -server should show requests being handled. - -If the LDAP server server is stopped, as with `CTRL-C`, then the database -contents will be lost. All of the steps above will have to be re-done the next -time the server is started. - -In order to make the LDAP database persistent, see the -https://github.com/osixia/docker-openldap[osixia/openldap -instructions]. - -As a final step you should <>. - -[#bootstrap_with_packages] -== Installing OpenLDAP from packages -=== Bootstrap -==== Switch to privileged user +== Switch to privileged user [source,shell] ---- @@ -122,7 +15,9 @@ For convenience we will configure the OLC database to be modifiable only by the root user (`UID=0`, `GID=0`). When configuring OpenLDAP via OLC the `ldapadd` utility will be run as root so our changes are authorised. -==== Install packages +== Bootstrap + +=== Install packages OpenLDAP LTB packages available under the "Packaging and OpenLDAP extensions" heading https://ltb-project.org/documentation[here]. @@ -130,7 +25,7 @@ OpenLDAP LTB packages available under the You should add the appropriate repository definitions and install the `openldap` package (`yum install openldap` or `apt-get install openldap`). -==== Define site specific variables +=== Define site specific variables Change the values here to match local paths and your site specific configuration. @@ -153,7 +48,7 @@ LDAP_ADMIN_PASSWORD="secret" LDAP_READONLY_PASSWORD="readonly" ---- -==== Switch OpenLDAP from using static configuration files to OLC +=== Switch OpenLDAP from using static configuration files to OLC After installing OpenLDAP, `slapd` (the main OpenLDAP daemon) needs to be switched to using OLC (on-line configuration). @@ -201,7 +96,7 @@ sed -ie 's#^SLAPD_CONF=.*#SLAPD_CONF=""#;s#^SLAPD_CONF_DIR=.*#SLAPD_CONF_DIR="$S systemctl start slapd ---- -==== Define a database and set appropriate ACLs +=== Define a database and set appropriate ACLs [source,shell] ---- @@ -224,8 +119,9 @@ olcAccess: to dn.base="" by * read EOF ---- -.Interpreting OpenLDAP ACLs -**** +.Wondering what those ACLs mean? +[%collapsible] +==== The OpenLDAP ACL syntax can be difficult to understand for new users. To help with implementing site-specific ACLs, the humanly readable translation of the @@ -242,10 +138,9 @@ base ACLs in the above example is included below. * `to dn.base="" by * read` ** Any user may access the metadata at the top of the directory. The is useful for the autodiscovery functionality in LDAP browsers. +==== -**** - -==== Populate the top level object, and add credentials for the readonly user +=== Populate the top level object, and add credentials for the readonly user [source,shell] ---- @@ -267,7 +162,7 @@ description: LDAP read only user EOF ---- -=== Loading schemas +== Load schemas For our tests we need to load some basic bundled OpenLDAP schemas and some FreeRADIUS specific schemas for defining profiles, RADIUS to LDAP mappings and @@ -290,7 +185,7 @@ for i in freeradius.ldif freeradius-clients.ldif; do done ---- -=== Test data +== Populate with test data Once slapd is running and appropriately configured with database definitions an admin user, a readonly user, and the prerequisite schemas, it can now be populated with test data. @@ -307,7 +202,7 @@ sed -e '1,/^description:/ d' ${FREERADIUS_SRC}/src/tests/modules/ldap/example.co | ldapadd -Y EXTERNAL -H ldapi://%2Fvar%2Frun%2Fslapd%2Fldapi ---- -=== Switch to an unprivileged user +== Switch to an unprivileged user [source,shell] ---- exit @@ -320,8 +215,7 @@ If, however, you need to make further changes to OpenLDAP's configuration, you should note that the `ldapadd`, `ldapmodify` commands must be called as root with the `-Y EXTERNAL` argument. -[#verifying_openldap_setup] -== Verifying OpenLDAP setup +== Check it works As a final step you should verify that test data has been loaded correctly. This can be done using the `ldapsearch` utility using the LDAP read only user. diff --git a/doc/antora/modules/howto/pages/modules/ldap/configuration.adoc b/doc/antora/modules/howto/pages/modules/ldap/configuration.adoc index 20eda3c8397..0abee266aed 100644 --- a/doc/antora/modules/howto/pages/modules/ldap/configuration.adoc +++ b/doc/antora/modules/howto/pages/modules/ldap/configuration.adoc @@ -1,7 +1,7 @@ = Enabling the LDAP module The configuration step should be little more than -xref:modules/ldap/ldapsearch.adoc#_translating_ldapsearch_arguments_to_rlm_ldap_configuration_items[copying the parameters used by ldapsearch] +xref:modules/ldap/ldapsearch/index.adoc#_translating_ldapsearch_arguments_to_rlm_ldap_configuration_items[copying the parameters used by ldapsearch] to the xref:raddb:mods-available/ldap.adoc[ldap module] configuration file. The xref:raddb:mods-available/ldap.adoc[ldap module] configuration file @@ -16,15 +16,15 @@ possible in the default configuration. The defaults are usually close to being correct. All that is necessary is to make minor changes, and _test_ them. FreeRADIUS should look for data. -If the xref:modules/ldap/search[ldapsearch] tests above pass, +If the xref:modules/ldap/ldapsearch/index.adoc[ldapsearch] tests above pass, then the LDAP module configuration can be copied directly from the command-line options to that tool using the -xref:modules/ldap/ldapsearch.adoc#_translating_ldapsearch_arguments_to_rlm_ldap_configuration_items[translation table] on that page. +xref:modules/ldap/ldapsearch/index.adoc#_translating_ldapsearch_arguments_to_rlm_ldap_configuration_items[translation table] on that page. At a minimum you will need to set the following configuration items in in xref:raddb:mods-available/ldap.adoc[mods-available/ldap] using the values you discovered -running the xref:modules/ldap/search[ldapsearch] tests. +running the xref:modules/ldap/ldapsearch/index.adoc[ldapsearch] tests. [source,config] ---- @@ -36,7 +36,7 @@ ldap { user { base_dn = "ou=people,${..base_dn}" <5> - filter = '(objectClass=posixUser)' <6> + filter = '(&(uid=%{%{Stripped-User-Name}:-%{User-Name}})(objectClass=posixUser))' <6> } } ---- @@ -51,7 +51,7 @@ ldap { to reduce network overhead. <4> The object containing all other objects we're interested in. <5> The object containing all the users we need to perform AAA functions for. -<6> Filter to select only valid user objects. +<6> Filter to select the object matching the authenticating user. We do _not_ recommend immediately configuring TLS unless you are testing against a production server. The best approach is to test one piece in isolation, diff --git a/doc/antora/modules/howto/pages/modules/ldap/index.adoc b/doc/antora/modules/howto/pages/modules/ldap/index.adoc index 614201a38b7..7f0bef1a61a 100644 --- a/doc/antora/modules/howto/pages/modules/ldap/index.adoc +++ b/doc/antora/modules/howto/pages/modules/ldap/index.adoc @@ -45,14 +45,14 @@ where a high level of redundancy is required. === 2. Testing Once an LDAP server is available, it should be tested via the command-line -xref:modules/ldap/ldapsearch.adoc[ldapsearch] tool. This is to ensure that +xref:modules/ldap/ldapsearch/index.adoc[ldapsearch] tool. This is to ensure that the LDAP server has been configured correctly. If testing via `ldapsearch` fails, then that *MUST* those issues must be resolved before configuring FreeRADIUS. === 3. Configuring the LDAP module -Once the xref:modules/ldap/ldapsearch.adoc[ldapsearch] validation tests pass, the +Once the xref:modules/ldap/ldapsearch/index.adoc[ldapsearch] validation tests pass, the next step is to xref:modules/ldap/configuration.adoc[configure the LDAP module]. OpenLDAP configuration examples detailing how to install appropriate schemas and diff --git a/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/before_starting.adoc b/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/before_starting.adoc new file mode 100644 index 00000000000..3f89b3b6309 --- /dev/null +++ b/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/before_starting.adoc @@ -0,0 +1,20 @@ += Before starting + +Try to determine answers to the following questions: + +* What's the IP address of the LDAP server being tested? +** If using an FQDN does the FQDN resolve to this IP address? +* Does the LDAP server requires encrypted connections? +** If yes: +*** Does the LDAP server use the StartTLS extension to transition to +encrypted communication, or is communication encrypted from the start (LDAPS)? +*** What are the necessary root certificates and intermediary certificates +needed to validate the identity of the LDAP server? +* What port is used to connect to the LDAP server? Usually this will be `389` +(unencrypted or StartTLS) or `636` (LDAPS). +* Do connections need to be bound to perform searches on the LDAP directory? +** If yes: +*** Determine what credentials are needed for binding, and whether a client +certificate is required. +* Determine the Base DN, the object deepest in the tree that contains the object +representing users, groups, clients etc... diff --git a/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/connection_parameters.adoc b/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/connection_parameters.adoc new file mode 100644 index 00000000000..85242d14459 --- /dev/null +++ b/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/connection_parameters.adoc @@ -0,0 +1,223 @@ += Determining connection parameters + +`ldapsearch` accepts a large number of different arguments, and allow relatively +complex commands to be sent to the LDAP server. + +It will likely take a number of attempts to find the correct `ldapsearch` invocation. +If you have no prior information about the LDAP server follow the examples below, +adding progressively more connection parameters until `ldapsearch` returns a positive +result (`$? == 0` and one or more entries written to stdout). + +[source,ldif] +---- +dn: uid=john,ou=people,dc=example,dc=com +objectClass: inetOrgPerson +uid: john +userPassword: password +---- + +If the `ldapsearch` program fails to return anything useful, then additional +arguments should be added until the search succeeds. Unfortunately, every piece +of advice in this section is site-specific, and is independent of RADIUS. + +== Search without bind (anonymous) +At a minimum, unless you have defaults set in your local `ldap.conf` file, +you will need to provide the following arguments: + +- `-z 1` - Limit the number of results to 1. +- `-x` - Use simple authentication instead of SASL (correct for the majority of servers). +- `-H ` - The LDAP server to connect to. +- `-b ` - A point in the LDAP tree to start the search from. + +.Performing an anonymous search +==== +[source,shell] +---- +ldapsearch -z 1 -x \ + -H ldap://ldap.example.com:389 \ + -b "cn=freeradius,dc=example,dc=com" +---- +==== + +== Failure - Bad FQDN/IP or attempt blocked by firewall + +``` +ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1) +``` + +* Verify (using `dig` or similar) that the FQDN of the LDAP +server can be resolved. +* Verify that there is no firewall blocking access. Some techniques to check this +which _may_ work. +** `sudo traceroute -Tn -p 636 ldap.example.com` will be successful (and not +continue indefinitely) when there is _no_ firewall blocking. +** `netcat -vz -w3 ldap.example.com 636` will return `succeeded` when there is no +firewall blocking access. + +== Failure - Bind required + +``` +ldap_bind: Server is unwilling to perform (53) + additional info: unauthenticated bind (DN with no password) disallowed +``` + +The majority of LDAP servers will also require the LDAP client to identify itself, +this is done with a bind operation. + +If a message similar to the one above is returned, it means a bind operation +must be performed before attempting any operations on the LDAP server. + +The following arguments should be added to identify the LDAP client to the LDAP +server: + +- `-D ` - The bind DN. A unique identifier for the user being bound. +- `-w ` - The bind password. + +.Performing a search with a bound user +==== +[source,shell] +---- +ldapsearch -z 1 -x \ + -H ldap://ldap.example.com:389 \ + -D "cn=freeradius,dc=example,dc=com" \ + -w mypassword \ + -b "dc=example,dc=com" +---- +==== + +== Failure - Encryption (confidentiality) required + +If a message similar to the one below is returned by `ldapsearch`. TLS must +be enabled on the connection. + +``` +ldap_bind: Confidentiality required (13) + additional info: TLS confidentiality required +``` + +If you're not sure which encryption method is used (LDAPS or StartTLS) try +each of them in turn. + +In order to configure either encryption method you will need to have acces +to the certificate chain for the certificate the LDAP server presents. + +See the section below on retrieving certificate chains if you don't have +access to these certificates. + +=== Common parameters for both LDAPS and StartTLS) + +- `LDAPTLS_CACERT=` - An environmental variable +specifying a PEM file containing CA and chain of trusted certificates, that +joins with the chain presented by the LDAP server. These certificates must be +provided in order to validate the LDAP server's certificate. + +- `LDAPTLS_REQCERT=hard` - An environmental variable requiring the LDAP server +provide a certificate. + +=== Encryption with LDAPS + +LDAPS is configured by changing the URI scheme passed as the value to `-H`. + +Where an unencrypted or StartTLS connection uses `ldap://`, LDAPS requires +`ldaps://`. + +The port for LDAPS is different than for unencrypted communication. Where an +unencrypted or StartTLS connection normally uses `TCP/389`, LDAPS normally uses +`TCP/636`. + +For LDAPS an unencrypted or StartTLS URI is changed from: +``` +-H ldap://:389 +``` + +to + +``` +-H ldaps://:636 +``` + +.Performing a search with a bound user with LDAPS +==== +[source,shell] +---- +LDAPTLS_CACERT=cert_bundle.pem LDAPTLS_REQCERT=hard \ + ldapsearch -z 1 -x \ + -H ldaps://ldap.example.com:636 \ + -D "cn=freeradius,dc=example,dc=com" \ + -w mypassword \ + -b "dc=example,dc=com" +---- +==== + +=== Encryption with StartTLS + +StartTLS connections runs on the same port as unencrypted LDAP. StartTLS +is an LDAP + +- `-ZZ` - Transition to encrypted communication using the StartTLS extension, +and fail if we can't. + +.Performing a search with a bound user with StartTLS +==== +[source,shell] +---- +LDAPTLS_CACERT=cert_bundle.pem LDAPTLS_REQCERT=hard \ + ldapsearch -z 1 -x \ + -H ldap://ldap.example.com:389 \ + -D "cn=freeradius,dc=example,dc=com" \ + -w mypassword \ + -b "dc=example,dc=com" \ + -ZZ +---- +==== + +.Retrieving certificate chains from the server +**** +If using LDAP over TLS `openssl s_client` can display information about the +certificates presented by the LDAP server. The information returned +(particularly the certificate issuer(s)) in useful to determine what +certificates need to be available to the LDAP client. + +The `openssl` invocation is different depending on whether StartTLS or LDAPS +is used. + +.LDAPS - Retrieving the certificate chain of the fictitious ldap.example.com server +==== +``` +echo -n | openssl s_client -host ldap.example.com -port 636 -prexit -showcerts +CONNECTED(00000003) +depth=1 C = OT, ST = Tentacle Cove, O = FreeRADIUS, OU = Services, CN = example.com, emailAddress = support@example.com +verify return:0 +--- +Certificate chain + 0 s:/C=OT/ST=Tentacle Cove/L=Grenoble/O=FreeRADIUS/OU=Services/CN=ldap.example.com/emailAddress=support@example.com + i:/C=OT/ST=Tentacle Cove/O=FreeRADIUS/OU=Services/CN=example.com/emailAddress=support@example.com +-----BEGIN CERTIFICATE----- +MIIHDjCCBPagAwIBAgIJANAO5znieeLNMA0GCSqGSIb3DQEBCwUAMIGSMQswCQYD +... +``` +==== + +.StartTLS - Retrieving the certificate chain of the fictitious ldap.example.com server +==== +``` +echo -n | openssl s_client -host ldap.example.com -port 389 -prexit -showcerts -starttls ldap +CONNECTED(00000003) +depth=1 C = OT, ST = Tentacle Cove, O = FreeRADIUS, OU = Services, CN = example.com, emailAddress = support@example.com +verify return:0 +--- +Certificate chain + 0 s:/C=OT/ST=Tentacle Cove/L=Grenoble/O=FreeRADIUS/OU=Services/CN=ldap.example.com/emailAddress=support@example.com + i:/C=OT/ST=Tentacle Cove/O=FreeRADIUS/OU=Services/CN=example.com/emailAddress=support@example.com +-----BEGIN CERTIFICATE----- +MIIHDjCCBPagAwIBAgIJANAO5znieeLNMA0GCSqGSIb3DQEBCwUAMIGSMQswCQYD +... +``` +[NOTE] +.Availability of `-starttls ldap` +Not all builds of `openssl s_client` support `-starttls ldap`. As of OpenSSL +1.1.1 this feature is still only available in the OpenSSL master branch. See +this GitHub Pull Request for details: +https://github.com/openssl/openssl/pull/2293. +==== +**** diff --git a/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/index.adoc b/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/index.adoc new file mode 100644 index 00000000000..6fdf4ba6da7 --- /dev/null +++ b/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/index.adoc @@ -0,0 +1,32 @@ += Mapping and testing with ldapsearch + +Every LDAP directory is different. Each directory has different connection parameters, +locations for objects, and attributes within those objects. + +This section of the LDAP howto will guide you through discovering the values +needed to configure the LDAP module. + +We _strongly_ recommend running these checks in this section on the same machine +that will be used to host the RADIUS server. A common cause of connectivity +issues is that a firewall is preventing network access from the RADIUS server to +the LDAP server. Running `ldapsearch` on a third machine may not reveal these +firewall issues. + +== xref:modules/ldap/ldapsearch/before_starting.adoc[Before starting] + +What questions to ask your LDAP administrator before you begin working with the +directory. + +== xref:modules/ldap/ldapsearch/connection_parameters.adoc[Determining connection parameters] + +Discover through trial and error, what connection parameters the LDAP +directory requires. + +== xref:modules/ldap/ldapsearch/locating_objects.adoc[Locating objects] + +Discover where users and groups are located within the directory. + +== xref:modules/ldap/ldapsearch/translating_to_the_ldap_module.adoc[Using results to configure the LDAP module] + +Translate LDAP search arguments and the results from previous sections +into LDAP module configuration items diff --git a/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/locating_objects.adoc b/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/locating_objects.adoc new file mode 100644 index 00000000000..52edb7262ac --- /dev/null +++ b/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/locating_objects.adoc @@ -0,0 +1,250 @@ += Locating objects + +Once the correct xref:modules/ldap/ldapsearch/connection_parameters.adoc[connection parameters] + have been determined, the next step in building your configuration +is to locate where user and group objects are stored in the directory. + +== How to locate user objects +Users are represented by a fairly limited subset of ObjectClasses. The +following filters are usually sufficient to identify users in different +directory types. + +- OpenLDAP - `(|(ObjectClass=organizationalPerson)(ObjectClass=posixAccount)(ObjectClass=Person))` +- ActiveDirectory - `(|(ObjectClass=User)(ObjectClass=posixAccount))` +- Novell eDir - `(ObjectClass=User)` + +.Performing a search with a user object filter +==== +``` +ldapsearch -z 10 -x -H ldap://ldap.example.com:389 -b "dc=example,dc=com" "(|(ObjectClass=organizationalPerson)(ObjectClass=PosixAccount)(ObjectClass=Person))" + +# extended LDIF +# +# LDAPv3 +# base with scope subtree +# filter: (|(ObjectClass=organizationalPerson)(ObjectClass=PosixAccount)(ObjectClass=Person)) +# requesting: ALL +# + +... + +# doctopus, octopuses, example.com +dn: uid=doctopus,ou=octopuses,dc=example,dc=com +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +cn: Doctopus McTentacles +sn: McTentacles +uid: dpus +givenName: Doctopus +userPassword:: MGN0MHB1NTNzUnVsMw== + +# search result +search: 3 +result: 0 Success + +# numResponses: 18 +# numEntries: 17 +``` +==== + +There's a lot of useful information in this result: + +- We know where the user objects are likely to be located, as it's fairly +obvious from the DN (`...,ou=octopuses,dc=example,dc=com`). _Note: In the +majority of non-FreeRADIUS themed LDAP directories the user object container +will be `ou=people`_. +- The result shows a `uid` attribute. This will almost always be the correct +attribute for identifying a user. +- The result shows a base64 encoded `userPassword` attribute. This +means users can be authenticated without rebinding the connection (more +efficient). +- There's no `memberOf` attributes. This means user to group mappings +are likely stored in group objects instead of the user objects themselves. + +=== What to record + +- `user_object_base_dn` - The DN higher in the tree than relevant users +objects. +- `user_object_class_filter` - The filter which matches the objectClass(es) of +user objects. +- `uid_attribute` - The attribute used to identify the user +(usually `uid`, but can vary considerably between instances). +- `user_password_attribute` - The attribute used to hold password data (if +present). +- `account_disabled_attribute` - Any attributes used to indicate whether an +account is disabled. To determine if this attribute exists, repeat the user +search (above) with a filter for a user account known to be disabled e.g. +`(uid=a-disabled-user)`. +- `account_enabled_attribute` - Any attributes used to indicate whether an +account is enabled. Should be present in the search results already obtained. + +.Locating users in "mature" LDAP directories +**** +The above result represents an ideal scenario. In reality, LDAP directories +often accumulate a lot of cruft. Users objects may be located in +multiple container objects, and at multiple levels within a directory. + +If you believe this to be the case with your directory, remove the `-z` argument +limiting the number of result, and pipe the output of `ldapsearch` through a +command chain _such as_: `grep dn: | sed -e 's/dn: [^,]*,//' | sort | uniq -c`. + +This operation will return a list of objects which _contain_ user objects, +and how many user objects they contain. + +.Determining which objects contain users +==== +``` +ldapsearch -x -H ldap://ldap.example.com:389 -b "dc=example,dc=com" "(|(ObjectClass=organizationalPerson)(ObjectClass=PosixAccount)(ObjectClass=Person))" | grep dn: | sed -e 's/dn: [^,]*,//' | sort | uniq -c` +2 ou=octopuses,dc=example,dc=com +10 ou=people,dc=example,dc=com +1 ou=people,dc=created-by-someone-who-didnt-know-what-dc-meant,dc=example,dc=com +2 ou=people,ou=remnant-of-an-ancient-edir-instance,dc=example,dc=com +2 ou=giraffes,dc=example,dc=com +``` +==== + +The user object base DN selected for your rlm_ldap configuration must be higher +in the object tree than the objects containing users. In the above example the +user object base DN would be `dc=example,dc=com`. + +If an error message is returned similar to `Size limit exceeded (4)`, it means +that the LDAP server is refusing to provide the complete set of results. If the +LDAP directory supports result paging, adding a paging request such as `-E +'pr=100'` may negate the per-search result limit. + +For large result sets you should also specify a list of attributes to retrieve +after the search filter. Limiting the attributes returned reduces load on the +LDAP server, and generally results in a quicker response. + +``` +ldapsearch ... -E 'pr=100' "(|(ObjectClass=...))" "dn" | ... +``` +**** + +== How to locate group objects + +Groups are represented by a fairly limited subset of ObjectClasses. The +following filters are usually sufficient to identify groups in different +directory types. + +- OpenLDAP - `(objectClass=groupOfNames)` +- ActiveDirectory - `(objectClass=group)` +- Novell eDir - `(objectClass=group)` + +.Performing a search with a group object filter +==== +``` +ldapsearch -z 10 -x -H ldap://ldap.example.com:389 -b "dc=example,dc=com" "(objectClass=posixGroup)" + +# extended LDIF +# +# LDAPv3 +# base with scope subtree +# filter: (ObjectClass=posixGroup) +# requesting: ALL +# + +... + +dn: cn=mariner-alert,ou=groups,dc=example,dc=com +cn: mariner-alert +description: Heads up to all delicious denizens of the directory +member: uid=doctopus,ou=octopuses,dc=example,dc=com +member: uid=rocktopus,ou=octopuses,dc=example,dc=com +objectClass: groupOfNames +objectClass: fdGroupMail +mail: mariner-alert@example.com + +# search result +search: 3 +result: 0 Success + +# numResponses: 10 +# numEntries: 9 +``` +==== + +.Locating groups in "mature" directories +**** +As with users, groups may be located in multiple areas of the directory. + +.Determining which objects contain groups +==== +``` +ldapsearch -x -H ldap://ldap.example.com:389 -b "dc=example,dc=com" -E 'pr=100' "(ObjectClass=posixGroup)" dn | grep dn: | sed -e 's/dn: [^,]*,//' | sort | uniq -c` +3 ou=groups,dc=example,dc=com +1 ou=groups,o=long-defunct-org-that-was-merged-with-example-com,dc=example,dc=com +3 ou=groups,ou=interns-summer-project,dc=example,dc=com +2 ou=groups,ou=backup-restored-in-the-wrong-place,dc=example,dc=com +``` +==== +**** + +=== Group membership scheme variants +Groups memberships can be represented in multiple ways. There are four main +variants: + +1. User objects which reference groups by DN, _usually_ with multiple +instances of the `memberOf` attribute. +2. User objects which reference groups by name, again, _usually_ +with multiple instances of the `memberOf` attribute. +3. Group objects which reference their members by DN, _usually_ with multiple +instances of the `member` attribute. +4. Group objects which reference their members by name, _usually_ with +multiple instances of the `memberUid` attribute. + +In order to perform efficient group lookups which group membership scheme the +LDAP server uses must be determined. + +Repeat the search for user objects using a filter that matches a user known +to be members of one or more groups e.g. +`(&(objectClass=posixUser)(uid=doctopus))`. + +- If the result shows a attribute containing the DN of known group, the LDAP +server implements _variant 1_. + +- If the result shows a attribute containing the name of known group, the LDAP +server implements _variant 2_. + +Repeat the the search for group objects using a filter that matches a group +the user is known to be a member of e.g. `(&(objectClass=groupOfNames)(cn=mariner-alert))`. + +- If the result shows an attribute containing the DN of a user, the ldap server +implements _variant 3_. + +- If the result shows an attribute containing the name of a user, the ldap server +implements _variant 4_. + +=== If the variant can't be determined + +- There's a typographical error in the search filter or `ldapsearch` arguments. +- The bound user doesn't have sufficient permission to view user or group +objects. Contact your LDAP administrator. +- The LDAP directory uses a special scheme to represent groups which requires +querying a special OID, or providing additional search controls (outside of the +scope of this howto). +- Group memberships are represented with 'operational' attributes. Repeat the +user object search with '+' set as the second positional argument. + +.Searching for a user object and returning operational attributes +``` +ldapsearch -z 10 -x -H ldap://ldap.example.com:389 -b "dc=example,dc=com" "(ObjectClass=posixUser)" + +``` + +=== What to record + +* `group_object_base_dn` - The DN higher in the tree than all +relevant group objects. +* `group_name_attribute` - The attribute used to identify the group +(usually `cn`). +* _variant 1_ +** `membership_attribute` - User object attribute containing group +membership information. +* _variant 2_ +** `membership_attribute` - User object attribute containing group +membership information. +* _variant 3_ +** `membership_user_dn_filter` - A filter matching users by DN. +* _vairant 4_ +** `membership_user_name_filter` - A filter matching users by user name. diff --git a/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/translating_to_the_ldap_module.adoc b/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/translating_to_the_ldap_module.adoc new file mode 100644 index 00000000000..f48d35405db --- /dev/null +++ b/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/translating_to_the_ldap_module.adoc @@ -0,0 +1,102 @@ += Translating ldapsearch arguments to LDAP configuration items + +[width="100%",cols="30%,20%,50%",options="header",] +|=== +| Purpose | ldapsearch argument | `ldap { ... }` config item +| Limit number of search results | ```-z``` | Not supported. +| Use basic authentication | ```-x``` | Set by default. +| LDAP Host URI | ```-H ``` | ```server = ''``` + + ```port = (389\|)``` +| Base DN | ```-b ``` | ```base_dn = ''``` +| Bind DN | ```-D ``` | ```identity = ''``` +| Bind Password | ```-w ``` | ```password = ''``` +| Enable LDAPS | ```-H ldaps://``` | ```server = 'ldaps://'``` + + ```port = (636\|)``` +| Enable StartTLS | ```-ZZ``` | ```tls { start_tls = yes }``` +| Specify RootCA and intermediaries | ```LDAPTLS_CACERT=``` | ```tls { ca_file = '' }``` +| Require cert validation to succeed | ```LDAPTLS_REQCERT=hard``` | ```tls { require_cert = 'demand' }``` +|=== + += Translating ldapsearch results to rlm_ldap configuration items + +== Users + +[width="100%",cols="30%,70%",options="header",] +|=== +| Purpose | `ldap { user { ... } }` config item +| Specify where to search for users | ```base_dn = ''``` +| Specify how to find a user | ```filter = "(&()(=%{%{Stripped-User-Name}:-%{User-Name}})"``` +| Retrieve a "known good" password | ```update { &control:Password-With-Header = ``` +| Allow accounts to be explicitly disabled | ```access_attribute = ''``` + + ```access_positive = 'no'``` +| Require accounts to be explicitly enabled | ```access_attribute = ''``` + + ```access_positive = 'yes'``` +|=== + +== Groups - Common + +[width="100%",cols="30%,70%",options="header",] +|=== +| Purpose | `ldap { group { ... } }` config item +| Specify where to search for group | ```base_dn = ''``` +| Specify which objects are groups | ```filter = ''``` +| Specify which attribute in a group object + identifies the group | ```name_attribute = ''``` +|=== + +== Groups - variant 1 + +User objects reference groups using DNs. + +[width="100%",cols="30%,70%",options="header",] +|=== +| Purpose | `ldap { group { ... } }` config item +| Specify how to find group objects by DN, when referenced by a user object. | ```membership_attribute = ''``` +|=== + +== Groups - variant 2 + +User objects reference groups using group names. + +[width="100%",cols="30%,70%",options="header",] +|=== +| Purpose | `ldap { group { ... } }` config item +| Specify how to find group objects by name, when referenced by a user object. | ```membership_attribute = ''``` +|=== + +== Groups - variant 3 + +Group objects reference users using DNs. + +[width="100%",cols="30%,70%",options="header",] +|=== +| Purpose | `ldap { group { ... } }` config item +| Specify how to find group objects referencing a user by DN. | ```membership_filter = "(=%{control:Ldap-UserDn})"``` +|=== + +== Groups - variant 4 + +Group objects reference users using user names. + +[width="100%",cols="30%,70%",options="header",] +|=== +| Purpose | `ldap { group { ... } }` config item +| Specify how to find group objects referencing a user by name. | ```membership_filter = "(=%{%{Stripped-User-Name}:-%{User-Name}})"``` +|=== + +.Mixing and matching group membership schemes +**** +Although rare, it is possible to have all four group membership scheme variants +in a single directory. FreeRADIUS supports this configuration. + +For _variant 1_ and _variant 2_ FreeRADIUS will automatically determine if the +user object attribute contained a DN or group name. + +For _variant 3_ and _variant 4_ it's possible to construct a filter which matches +both on user DN and user name e.g. + +[source,config] +---- +membership_filter = "(|(=%{control:Ldap-UserDn})(=%{%{Stripped-User-Name}:-%{User-Name}}))" +---- +****