#########
LDAP Hook
#########

The Stork server can use an external LDAP server to identify and authorize users and groups. It is
typically used to provide single-sign-on (SSO) via an organization's personnel directory. If you are not familiar with
LDAP, a `brief introduction <https://kb.isc.org/docs/ldap-intro>`_ is available.

LDAP support in Stork is provided by the ``stork-hook-ldap`` hook (plugin/library).

Installation
============

The recommended way to install the LDAP hook, and Stork itself, is to use the pre-compiled packages provided by ISC.
See :ref:`install-pkgs`.

If building from source, you must clone the repository or download the source tarball from `the LDAP hook GitLab project
<https://gitlab.isc.org/isc-projects/stork-hook-ldap>`_.  See also :ref:`installation_sources`.

If installing manually, please copy ``stork-hook-ldap.so`` to
the server hook directory (``/usr/lib/stork-server/hooks`` by default). The Stork server automatically loads any hooks
it finds there.

Configuration
=============

The LDAP hook has a number of configuration options controlling its behavior. They are specified by command-line options or
environment variables when the ``stork-server`` process is started.  This document generally gives the environment form, but you
can use either method.  Most commonly, the environment variables are specified in the ``/etc/stork/server.env`` file.  If both
command-line and environment variable are provided for the same option, the value from the command-line will be used.  See :ref:`server-setup` for more on configuring Stork.

If you are unsure what values to use, consult your LDAP directory administrator or vendor.

Option Summary
--------------

The following table lists each option, providing the full environment variable name, as well as the form of the
corresponding command-line option.  Further detail is provided in later sections.

.. list-table:: LDAP Hook Options
  :header-rows: 1

  * - Environment Variable
    - Command-Line Option
    - Description
    - Default Value
  * - ``STORK_SERVER_HOOK_LDAP_DEBUG``
    - ``--ldap.debug``
    - Log details of LDAP transactions? May include passwords.
    - ``FALSE``
  * - ``STORK_SERVER_HOOK_LDAP_URL``
    - ``--ldap.url=``
    - URL of LDAP server to query
    - ``ldap://127.0.0.1:1389``
  * - ``STORK_SERVER_HOOK_LDAP_TIMEOUT``
    - ``--ldap.timeout=``
    - How long to wait for a reply to each LDAP query/message
    - ``30s``
  * - ``STORK_SERVER_HOOK_LDAP_SKIP_SERVER_TLS_VERIFICATION``
    - ``--ldap.skip-tls-server-verification``
    - Disable checking of LDAP server certificate validity
    - ``FALSE``
  * - ``STORK_SERVER_HOOK_LDAP_BIND_USERDN``
    - ``--ldap.bind-userdn=``
    - DN of account used by Stork to access the LDAP server
    - ``cn=admin,dc=example,dc=org``
  * - ``STORK_SERVER_HOOK_LDAP_BIND_PASSWORD``
    - ``--ldap.bind-password=``
    - Password for USERDN
    - ``adminpassword``
  * - ``STORK_SERVER_HOOK_LDAP_ROOT``
    - ``--ldap.root=``
    - DN context used as the base for all queries
    - ``dc=example,dc=org``
  * - ``STORK_SERVER_HOOK_LDAP_GROUP_ALLOW``
    - ``--ldap.group-allow=``
    - Group that a user must be a member of to login to Stork
    - (empty)
  * - ``STORK_SERVER_HOOK_LDAP_MAP_GROUPS``
    - ``--ldap.map-groups``
    - Map LDAP groups to Stork roles?
    - ``FALSE``
  * - ``STORK_SERVER_HOOK_LDAP_GROUP_ADMIN``
    - ``--ldap.group-admin=``
    - CN of the LDAP group corresponding to Stork 'admin' role
    - ``stork-admin``
  * - ``STORK_SERVER_HOOK_LDAP_GROUP_SUPER_ADMIN``
    - ``--ldap.group-super-admin=``
    - CN of the LDAP group corresponding to Stork 'super-admin' role
    - ``stork-super-admin``
  * - ``STORK_SERVER_HOOK_LDAP_GROUP_READ_ONLY``
    - ``--ldap.group-read-only=``
    - CN of the group corresponding to Stork 'read-only' role
    - ``stork-read-only``
  * - ``STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_GROUP``
    - ``--ldap.object-class-group=``
    - Class name, for groups
    - ``groupOfNames``
  * - ``STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_GROUP_MEMBER``
    - ``--ldap.object-class-group-member=``
    - Property name, in the group class, for the list of member users
    - ``member``
  * - ``STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_GROUP_COMMON_NAME``
    - ``--ldap.object-class-group-common-name=``
    - Property name, in the group class, for the group's name
    - ``cn``
  * - ``STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_USER``
    - ``--ldap.object-class-user=``
    - Class name, for users
    - ``organizationalPerson``
  * - ``STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_USER_ID``
    - ``--ldap.object-class-user-id=``
    - Property name, in the user class, for a user's login name
    - ``uid``
  * - ``STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_USER_FIRST_NAME``
    - ``--ldap.object-class-user-first-name=``
    - Property name, in the user class, for a user's first name
    - ``givenName``
  * - ``STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_USER_LAST_NAME``
    - ``--ldap.object-class-user-last-name=``
    - Property name, in the user class, for a user's last name
    - ``sn``
  * - ``STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_USER_EMAIL``
    - ``--ldap.object-class-user-email=``
    - Property name, in the user class, for a user's email address
    - ``mail``

Connection to LDAP Server
-------------------------

You must configure a connection from Stork to the LDAP server. This is used by Stork to connect to the directory,
make queries, and verify passwords.

- ``STORK_SERVER_HOOK_LDAP_URL``

    Specify the URI of your LDAP server.  It must include the protocol, either ``ldap`` or ``ldaps``.  Port number is
    optional; the defaults are 389 and 636, respectively.  For example, ``ldap://nyc-dc1.example.com`` or
    ``ldaps://198.51.100.42:636``.

- ``STORK_SERVER_HOOK_LDAP_BIND_USERDN``

    Provide the user identity Stork will use to login to the LDAP server (also called binding). You must provide a fully
    qualified Distinguished Name (DN). This is not necessarily a regular user, nor a user of Stork. The DN context of
    this user may differ from ``STORK_SERVER_HOOK_LDAP_ROOT``.

- ``STORK_SERVER_HOOK_LDAP_BIND_PASSWORD``

    Indicate the password for the user given in ``STORK_SERVER_HOOK_LDAP_BIND_USERDN``. If the password contains characters
    special to the Unix shell, they may need to be quoted, or escaped with a backslash.

LDAP Structure
--------------

You must tell Stork how to find objects in the directory.

- ``STORK_SERVER_HOOK_LDAP_ROOT``

    Specify an LDAP naming context used as the base DN for all LDAP queries.  You must provide a fully qualified
    Distinguished Name (DN).  You may use the root of your directory.  For example, ``DC=example,DC=com``.  If all Stork
    users and groups can be found within a particular Organizational Unit, limiting the context can improve performance.
    For example, ``OU=InfoTech,OU=Widgets Division,DC=example,DC=com``.

Nested Groups
-------------

If you plan to use LDAP groups with Stork, you should be aware of the following limitation.

Some LDAP implementations allow groups to be nested.  That is, one group can be placed inside another group.  For example, if a group ``routeradmins`` is made a member of a group ``netadmins``, then any user placed in the ``routeradmins`` group should automatically become a member of the ``netadmins`` group.

As of this writing, Stork does not recognize nested group memberships.  Stork will only see users who are directly placed in any groups you specify.  To continue our example, if Stork was configured to use ``netadmins``, then placing a user in the ``routeradmins`` group would not work for Stork.  The users would have to be placed in the ``netadmins`` group as well.

This may change in a future release of Stork.
    
User Access
-----------
    
- ``STORK_SERVER_HOOK_LDAP_GROUP_ALLOW``

    If defined, Stork login will be allowed only for members of the specified LDAP group.  You must provide the Common
    Name of the group (without ``CN=`` prefix).  Use this if not all LDAP users should be permitted login to Stork.  For
    example, if you have an LDAP group ``netadmins``, you might specify that here, and then only your network admins
    will be able to login to Stork.  The default is empty, which allows any LDAP user (who can authenticate with their
    password) to login to Stork.

    This is independent of the group mapping feature (``STORK_SERVER_HOOK_LDAP_MAP_GROUPS``).  You can restrict Stork login
    without mapping Stork roles.  You can map Stork roles without restricting Stork login.  However, it often makes the
    most sense to use both features together.

Group Mappings
--------------

Optionally, you can configure mappings between LDAP groups and the Stork roles. This allows Stork to automatically
follow permission changes in your organization's directory.

- ``STORK_SERVER_HOOK_LDAP_MAP_GROUPS``

    First, this must be set to true to enable group-to-role mapping.  True values include ``true`` and ``1``.  The
    default is false (mapping is disabled).  If group mapping is disabled, all LDAP users will be assigned the
    "read-only" role in Stork.  If group mapping is enabled, and a given LDAP user is permitted login but is not a
    member of any mapped group, they will be assigned no Stork role, and will receive error messages if they try to use
    Stork.

- ``STORK_SERVER_HOOK_LDAP_GROUP_SUPER_ADMIN``

    Members of this LDAP group will be granted the super-admin role in Stork. Specify the Common Name of an LDAP group
    (without CN= prefix).  The default is ``stork-super-admin``.

- ``STORK_SERVER_HOOK_LDAP_GROUP_ADMIN``

    Members of this LDAP group will be granted the admin role in Stork. Specify the Common Name of an LDAP group
    (without ``CN=`` prefix).  The default is ``stork-admin``.

- ``STORK_SERVER_HOOK_LDAP_GROUP_READ_ONLY``

    Members of this LDAP group will be granted the read-only role in Stork. Specify the Common Name of an LDAP group
    (without ``CN=`` prefix). The default is ``stork-read-only``.


LDAP Schema
-----------

LDAP directories can vary in the names used for classes and attributes.  Thus, Stork allows you to define the names used
for all such items.  These values are used by Stork to build the LDAP queries.

- ``STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_GROUP``

    This is the name of the LDAP class which is used to represent groups.  Here, a "group" is a named collection of
    users who are assigned the same role or permissions.  They are sometimes called "security groups" or "user groups".
    This is the name of the class or type for the thing Stork calls a "group" (not the name of a particular group).
    Stork will look for groups by querying for objects of this class.

    The default is ``groupOfNames``.  Other common names are ``groupOfUniqueNames`` and just ``group``.  Notably,
    Microsoft Active Directory uses ``group``.
  
- ``STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_GROUP_MEMBER``

    This is the name of the property, of the group class, which contains the member list for a group.  Stork will look
    for a group's members by querying for this property.

    The default is ``member``.  Another common property name is ``uniqueMember``.
  
- ``STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_GROUP_COMMON_NAME``

    This is the name of the property, of the group class, which contains the Common Name of the group.  When searching
    for a group by name, or checking for a matching group name, Stork will query for this property.  The default is
    ``cn``.
  
- ``STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_USER``

    This is the name of the LDAP class which is used to represent users.  Here, a "user" is a named person or account.
    This is the name of the class or type for the thing Stork calls a "user" (not the name of a particular user).
    Stork will look for users by querying for objects of this class.  The default is ``organizationalPerson``.
  
- ``STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_USER_ID``

    This is the name of the property, of the user class, which contains the user ID.  This is also called the "user
    name", "account name", "logon name", "login name", "login ID", or similar variations.  (This is **not** the numeric
    ``uid`` as used by Unix-like systems.)

    The default is ``uid``.  Microsoft Active Directory requires specifying ``sAMAccountName`` here (the odd
    capitalization is correct).
  
- ``STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_USER_FIRST_NAME``

    This is the name of the property, of the user class, which contains the user's first name(s).  First names are also
    called "given names", and in some languages, may not actually be first.  The default is ``givenName``.
  
- ``STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_USER_LAST_NAME``

    This is the name of the property, of the user class, which contains the user's last name(s).  Last names are also
    called "surnames" or "family names", and in some languages, may not actually be last.  The default is ``sn``.

- ``STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_USER_EMAIL``

    This is the name of the property, of the user class, which contains the user's Internet email address.  The default
    is ``mail``.

Diagnostics
-----------

- ``STORK_SERVER_HOOK_LDAP_TIMEOUT``

    This species how many seconds after sending a request to the LDAP server that Stork should wait for a response,
    before giving up and failing the LDAP login.  The default of 30 seconds should be more than enough in normal
    circumstances.
  
- ``STORK_SERVER_HOOK_LDAP_DEBUG``

    Set this to a true value (``1`` or ``true``) to have the LDAP hook log details of the LDAP protocol messages it
    exchanges with the LDAP server.  This can help you determine exactly where things are going wrong.

    Normally, this should be kept disabled (``0`` or ``false``), the default.

.. warning::
    
    Debugging logs details of every LDAP message.  That can include passwords (in the clear) and other
    sensitive information.  If enabling debugging, take care that logs are secured appropriately.

- ``STORK_SERVER_HOOK_LDAP_SKIP_SERVER_TLS_VERIFICATION``

    Normally, when an LDAP server presents an X.509-certificate for SSL/TLS authentication (LDAPS), Stork asks the
    TLS library to verify the certificate is trustworthy.  This typically means the certificate needs to be signed by
    a trusted Certificate Authority (CA) (or an intermediary with a CA signature).  Setting this option to true will
    cause the LDAP hook to accept any certificate from an LDAP server, even if the certificate is invalid.  This can
    be useful in lab or testing scenarios.

    Normally, this should be kept disabled (``0`` or ``false``), the default.

.. warning::
    
    Enabling this option may make Stork vulnerable to spoofing of the LDAP server, which could in turn be used to
    harvest passwords.  This option should only be used under controlled conditions.

Usage
=====

    Once you have configured Stork to use LDAP hook, the new "LDAP" authentication option will appear on the Stork login
    page.  Use the "Pick a Method" drop down list to select "LDAP". Enter user name and password.  Stork will use the
    hook to contact the LDAP server, validate the password, and log in the user to Stork.

  Internal users (created and managed from within the Stork web UI) can be used along with LDAP users.  Simply select the appropriate choice in the drop down list at logon.
    
Examples
========

Simple LDAP Integration
-----------------------

    The following is an example of what a very simple ``/etc/stork/server.env`` with LDAP might look like.  This would
    allow any user recognized by LDAP to login to Stork with the read-only role.

.. code-block::

    STORK_DATABASE_PASSWORD=DoNotUseExamplePasswords
    STORK_SERVER_HOOK_LDAP_URL=ldaps://198.51.100.42:636
    STORK_SERVER_HOOK_LDAP_BIND_USERDN=CN=ldapguest,DC=example,DC=net
    STORK_SERVER_HOOK_LDAP_BIND_PASSWORD=hunter2
    STORK_SERVER_HOOK_LDAP_ROOT=DC=example,DC=net

Microsoft Active Directory
--------------------------

    The following is an example of what a ``/etc/stork/server.env`` could look like, for an LDAP integration with
    Microsoft Active Directory.  This demonstrates most of the features described above, including optional features.
    Only members of the ``netadmins`` group can login to Stork, and their Stork role is based on membership in the
    ``storkadmins`` and ``storksupers`` groups.

.. code-block::

    STORK_DATABASE_PASSWORD=DoNotUseExamplePasswords
    STORK_SERVER_HOOK_LDAP_DEBUG=false
    STORK_SERVER_HOOK_LDAP_URL=ldap://nyc-dc1.example.com
    STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_USER_ID=sAMAccountName
    STORK_SERVER_HOOK_LDAP_OBJECT_CLASS_GROUP=group
    STORK_SERVER_HOOK_LDAP_BIND_USERDN=CN=ADQUERY,OU=Central IT,DC=example,DC=com
    STORK_SERVER_HOOK_LDAP_BIND_PASSWORD='battery horse correct staple'
    STORK_SERVER_HOOK_LDAP_ROOT=OU=InfoTech,OU=Widgets Division,DC=example,DC=com
    STORK_SERVER_HOOK_LDAP_GROUP_ALLOW=netadmins
    STORK_SERVER_HOOK_LDAP_MAP_GROUPS=true
    STORK_SERVER_HOOK_LDAP_GROUP_ADMIN=storkadmins
    STORK_SERVER_HOOK_LDAP_GROUP_SUPER_ADMIN=storksuperadmins
    STORK_SERVER_HOOK_LDAP_GROUP_READ_ONLY=storkreaders

See Also
========

- `stork-hook-ldap <https://gitlab.isc.org/isc-projects/stork-hook-ldap/>`_ on the ISC GitLab
-  `A Brief Introduction to LDAP <https://kb.isc.org/docs/ldap-intro>`_ on the ISC Knowledge Base
