Greetings, In 2015 IBM Cloudant developed an LDAP based authentication handler for its CouchDB 2.x-based Cloudant Local offering. Since then, it has been used in production on several large Cloudant Local deployments, accruing many bug fixes and enhancements in the process.
Over the years, there has clearly been interest in using LDAP with CouchDB [1], so it seems like the ldap_auth functionality might be something the greater community could benefit from. If there are no objections from the community or PMC, I'd be happy to open a PR in the hopes of getting it included in CouchDB 3.2. To give an idea of what it's all about, I've included the README.md contents below. Thanks, Jay [1] https://couchdb.markmail.org/search/?q=LDAP README.md: # Delegating Basic and Cookie authentication and authorization to LDAP CouchDB includes a built-in security model, with self-contained authentication and authorization which rely on .ini files and/or user databases to store user data, including usernames (uids), password hashes, and user roles for accessing database resources. For an organization which already uses an LDAP service for access control, it may make more sense to delegate authentication and authorization services to LDAP when accessing CouchDB. This is where `ldap_auth` comes in. At a high level, when `ldap_auth` is configured, it completely replaces the default Basic and Cookie authentication and authorization. It ignores .ini files and user databases altogether, and attempts to validate user credentials using the configured LDAP service. Once credentials have been authentication by the LDAP service, `ldap_auth` determines the user's roles (which in turn determine what the user is authorized to access) based on the LDAP groups of which the user is a member. ## ldap_interface This low-level interface module uses [eldap](http://www.erlang.org/doc/man/eldap.html) to connect to, authenticate, and search LDAP server(s) which are configured to model user accounts and their associated CouchDB roles. ### Configuration All configuration is done via `.ini` file, mostly in the `[ldap_auth]` section. The following parameters can be modified, and have the associated defaults in parentheses: - `servers (127.0.0.1)` one or more LDAP servers; defaults to a single host, but could be a comma separated list (note that all servers must use the same port, a limitation of the underlying eldap library) - `port (389)` LDAP server port for un-encrypted communication - `ssl_port (636)` LDAP server port for encrypted communication - `use_ssl (true)` if `true`, use TLS to encrypt traffic to LDAP servers - `timeout (5000)` milliseconds to wait for a response from an LDAP server before throwing an error - `user_base_dn (ou=users,dc=example,dc=com)` defines a directory location to start searching for users - `user_classes (person)` defines which `objectClass`es indicate a particular entry as a user during search - `user_uid_attribute (uid)` defines which attribute maps to username - `group_base_dn (ou=groups,dc=example,dc=com)` defines directory location to start searching for groups - `group_classes (posixGroup)` defines which `objectClass`es indicate a particular entry as a group during search - `group_member_attribute (memberUid)` defines which group attribute maps to user Uid - `group_role_attribute (description)` defines which group attribute maps to a particular role - `searcher_dn (uid=ldapsearch,ou=users,dc=example,dc=com)` defines the DN to use when searching for users and groups - `searcher_password (secret)` defines the password for the `searcher_dn` above - `user_bind_dns ([])` defines one or more base DNs into which the authenticating user's username can be inserted as the `user_uid_attribute`, and used to bind directly. See the "Efficiency Considerations" section below for details **Please note** that at least one of the above parameters must be set inside the `[ldap_auth]` section of a .ini configuration file in order for ldap_auth to be considered "configured", and to function as an authentication/authorization handler. Failure to explicitly set at least one `[ldap_auth]` parameter will result in the system using the default basic and cookie authentication/authorization handlers instead. ### Efficiency Considerations `ldap_auth` effectively has 2 modes of operation, depending on whether `user_bind_dns` has been defined or not. Both modes return a list of roles associated with a user upon authentication success. By default, `ldap_auth` opens a connection to an LDAP server and binds the connection handle with the searcher DN and password. It then uses that connection to search for a user record with the authenticating username. If a matching user DN is found, it opens a second connection to the server and attempts to bind that connection using the username and password credentials. If the bind is successful, it closes that connection, and uses the original "searcher" bound connection to search for groups which contain the `group_member_attribute` matching the user's username. Those groups have a `group_role_attribute` which indicates the actual roles for the user. On the other hand, if you know in advance that all your users' DNs can be constructed by inserting their usernames into the following pattern: `$uid_attribute=$username,$user_bind_dn` (e.g. `uid=jay,ou=users,dc=example,dc=com`), then you can configure one or more `user_bind_dns`, and `ldap_auth` will not make use of the searcher DN, nor `user_base_dn`, but instead attempt to bind directly with the constructed user DNs. If the bind is successful, that bound connection is further used to make the same group search as described in the above paragraph. Overall, this technique can be used to eliminate several additional network round trips. ## ldap_auth Implements Basic and Cookie based authentication handlers, using `ldap_interface:authorized_roles(Username, Password)` to obtain the roles associated with a particular user. Using `ldap_auth` with basic authentication requires no client side changes. A properly configured database will automatically attempt to authenticate via LDAP using the supplied credentials. Similarly, for cookie authentication, POST credentials to the `_session` endpoint to obtain an AuthSession cookie, which contains a signed hash of its contents: name, time issued, and authorized roles. Subsequent requests using that cookie will automatically use the roles in the cookie until it expires. The following configuration parameters in the `[couch_httpd_auth]` section are used: - `timeout (600)` seconds until the AuthSession cookie expires - `secret` the shared secret used to sign the AuthSession cookie, it should be created when the cluster is provisioned