---
title: "LDAP and Active Directory"
description: ""
url: "https://docs.itrsgroup.com/docs/opsview/current/configuration/users-and-roles/ldap-active-directory/index.html"
---
Opsview Monitor supports authentication of Users from LDAP or Active Directory (AD). However, each User needs to be configured in Opsview so that the correct access control is given to the User.

There are two stages:

- [Set up an LDAP realm](#setting-up-an-ldap-realm), so logins will be directed to your LDAP or AD server.
- [Configure a synchronization script](#synchronization-script) to create Users in Opsview Monitor based on LDAP group membership. This also controls authorization by assigning the User's Role based on the group in which they are a member.

If you are not using the [synchronization script](#synchronization-script) below, you should create Users in Opsview Monitor with a username that matches the User's LDAP account name, as this will not be done automatically.

## Setting up an LDAP realm

To configure LDAP or AD authentication, add the configuration to the file `/opt/opsview/deploy/etc/user_vars.yml`. Below are example configurations for [Active Directory](#active-directory) and [OpenLDAP](#openldap).

Once the changes have been made to `/opt/opsview/deploy/etc/user_vars.yml`, you must run the following command as the `root` user:

  ```
  /opt/opsview/deploy/bin/opsview-deploy /opt/opsview/deploy/lib/playbooks/orchestrator-install.yml
  ```


**Warning**


Do not edit `/opt/opsview/webapp/opsview_web_local.yml` or `/opt/opsview/webapp/opsview_web.yml`. These are Ansible-managed files that get overwritten on upgrades or updates.




**Note**


Make sure `/opt/opsview/webapp/opsview_web_local.yml` or `/opt/opsview/webapp/opsview_web.yml` is created with read permissions for the `opsview` user.




**Note**


YAML files are very particular about whitespace. Do not use tabs; there must be spaces to structure the file.



### Active Directory

The `user_vars.yml` file needs to have the following appended to it for Active Directory:

```
opsview_core_web_local_overrides:
  authentication:
    realms:
      ldap:
        credential:
          class: Password
          password_field: password
          password_type: self_check
        store:
          class: LDAP
          ldap_server: ldap.company.com
          ldap_server_options:
            timeout: 30
          binddn: anonymous # set this to your own LDAP user
          bindpw: secret    # set this to the password of the user
          # Use bindpw_encrypted instead of bindpw if you want an encrypted
          # value. Use the '/opt/opsview/coreutils/bin/opsview_crypt' tool to
          # generate the value
          #bindpw_encrypted: 88ff95374e563866f6c3c2af4517979a3dc14ea3cbdadb8684ada121e210e818
          start_tls: 0
          user_basedn: cn=Users,dc=ldap,dc=company,dc=com
          user_filter: (&(objectClass=user)(sAMAccountName=%s))
          user_scope: sub
          user_field: samaccountname
          user_search_options:
            deref: always
          use_roles: 0
```

The `user_field` must be `samaccountname` in lowercase. If you set the `user_field` to `sAMAccountName`, you could get this error:

  ```
  Deep recursion on subroutine "Catalyst::Authentication::Store::LDAP::User::stringify" at /opt/opsview/perl/lib/Catalyst/Authentication/Store/LDAP/User.pm line 290, <DATA> line 466.
  ```

### OpenLDAP

For OpenLDAP, the `/opt/opsview/deploy/etc/user_vars.yml` file needs to have the following appended:

```
opsview_core_web_local_overrides:
  authentication:
    realms:
      ldap:
        credential:
          class: Password
          password_field: password
          password_type: self_check
        store:
          class: LDAP
          ldap_server: localhost
          ldap_server_options:
            timeout: 30
          binddn: anonymous # set this to your own LDAP user
          bindpw: secret          # set this to the password of the user
          # Use bindpw_encrypted instead of bindpw if you want an encrypted
          # value. Use the '/opt/opsview/coreutils/bin/opsview_crypt' tool to
          # generate the value
          #bindpw_encrypted: 88ff95374e563866f6c3c2af4517979a3dc14ea3cbdadb8684ada121e210e818
          start_tls: 0
          user_basedn: ou=People,dc=ldap,dc=company,dc=com
          user_filter: (&(objectClass=posixAccount)(objectClass=inetOrgPerson)(uid=%s))
          user_scope: sub
          user_field: uid
          user_search_options:
            deref: always
          use_roles: 0
```

### System user

The LDAP server must have a system user defined that can query specific base DNs. The authentication works by initially binding to LDAP with the `binddn` user, and searching for an appropriate user account based on the `user_field` attribute. It then rebinds to LDAP with that user's DN and the given password. In the above example, the `binddn` user is `anonymous` with a `bindpw` password of `secret`, although this can be changed. The `binddn` user needs appropriate permissions to search for other users, and to search for group memberships.

### Config variations

Depending on your LDAP configuration, your `user_basedn`, `group_basedn` and `user_field` may vary. Also, the basedns may be `ou` rather than `cn` depending on your LDAP tree.

The `user_scope` or `group_scope` defines how far down in the LDAP tree to search. A setting of one means only search down one level. A setting of `sub` means to search all levels below. You may need to set to `sub` to find the appropriate entries.

### Multiple realms

This is possible by adding another section under `realms` with the same indentation as the initial `ldap` configuration. The realm names must be unique. With the examples above, if you are adding LDAP for another department or country, you may use similar names, such as `ldap_noc` or `ldap_usa`.


**Note**


When there is more than one realm defined, there is an extra drop-down item in the User edit page to select which realm to use on a per-user basis. The User is then authenticated via that particular realm in the future.



### Backup servers

To define backup LDAP servers, the `ldap_server` entry should be changed from:

  ```
  ldap_server: ldap.company.com
  ```

to:

  ```
  ldap_server: 
    - ldap.company.com
    - backup-ldap.company.com
  ```

The first server address connected to successfully will be used.


**Note**


Ensure you keep the indentation correct.



### Using TLS

To use `start_tls`, add some additional entries, as follows (indentation is important):

  ```
          start_tls: 1
          start_tls_options:
            verify: required
            cafile: /opt/opsview/ca.pem
  ```

Further available options are listed at [Net LDAP](https://metacpan.org/dist/perl-ldap/view/lib/Net/LDAP.pod#start_tls).

### Using LDAPS

To use LDAPS, you need to use the common name (FQDN) of the LDAP server instead of its IP address. Some additional entries are required under the `ldap_server_options` section:

  ```
  ldap_server_options:
    timeout: 30

  ```

changes to:

  ```
  ldap_server_options:
    timeout: 30
    scheme: ldaps
    cafile: /path/to/ssl/certs/certificate.csr
    verify: require
  ```

The verify field can be set to `none`, `optional`, or `require` with the most secure option being `require`.

When configured, you can run `sudo -u opsview /opt/opsview/webapp/bin/opsview_sync_ldap -t` to test the connection with your LDAP server.

### Restarting Opsview Web

To make the LDAP/AD authentication realm available in the web user interface (UI), restart Opsview Web:

  ```
  sudo /opt/opsview/coreutils/bin/opsview_watchdog opsview-web restart
  ```

## Synchronization script

The `/opt/opsview/webapp/bin/opsview_sync_ldap` script can be run as the `opsview` user to make changes to the list of Users based on the LDAP directory. This includes which role they have for authorisation purposes. We recommend setting up a cron job to run this script regularly to pick up updates.

The script goes through the following steps:

  1. Parse LDAP connect information from `opsview_web.yml` and `opsview_web_local.yml`.
  2. Connect to LDAP with the specified `binddn` and `bindpw`.
  3. Read each group definition file from the specified `group_dir` directory.
  4. For each group, get a list of Users in the group. Find those Users and expand the group XML file based on attributes for that User.
  5. If specified at the command line, commit changes and remove Users that do not belong to any of the groups (`-y` flag).
  6. By default, the script will automatically apply committed changes. If the `-n` flag was used to prevent applying changes, and a change has been made, go to **Configuration > Apply Changes** to use the new configuration in production.
  7. Script progress and changes committed will be logged to both syslog and the Audit Log. Logging verbosity can be controlled by changing the [Web App Log Level](https://docs.itrsgroup.com/docs/opsview/current/administration/distributing-functionality/opsview-web-app/index.html#logging) or the `-D` flag.

This will create Users and set their contact information based on the XML data. If the User already exists (based on the username), then their details will be updated.


**Note**


If you have manually added Users into Opsview Monitor for the LDAP realm and then wish to use the sync script, run it without the `-y` option (to ignore making changes). This is done because it could remove existing Users if your group permissions in LDAP have not been set up correctly.




**Member Limit**


There is a limit to how many members can be synchronized from a particular group -- split members among multiple groups if you hit the limit and if you see the "Got too many members listed for the group" message logged.



Use the `-h` flag for a full description of all script options.

### Add configuration

Update `/opt/opsview/deploy/etc/user_vars.yml` by adding in the appropriate `opsview_sync` section:

```
opsview_core_web_local_overrides:
  authentication:
    realms:
      ldap:
        credential:
          class: Password
          password_field: password
          password_type: self_check
        store:
          class: LDAP
          ldap_server: XXXXX
          ldap_server_options:
            timeout: 30
          binddn: XXXXX
          bindpw: XXXXX
          start_tls: 0
          user_basedn: cn=XXXXX,dc=ldap,dc=company,dc=com
          user_filter: XXXXX
          user_scope: XXXXX
          user_field: XXXXX
          user_search_options:
            deref: always
          use_roles: 0
          #
          # Use this template for Active Directory, or...
          opsview_sync:
            group_dir: /opt/opsview/webapp/etc/ldap
            group_basedn: cn=Users,dc=ldap,dc=company,dc=com
            group_filter: (&(objectClass=group)(cn=%s))
            group_scope: one
          #
          # Use this template for OpenLDAP. But only one!
          opsview_sync:
            group_dir: /opt/opsview/webapp/etc/ldap
            group_member_field: memberUid
            group_basedn: ou=Group,dc=ldap,dc=company,dc=com
            group_filter: (&(objectClass=posixGroup)(cn=%s))
            group_scope: sub
```

Use only one template. Ensure the `group_dir`, `group_member_field` (for OpenLDAP), `group_basedn`, `group_filter`, and `group_scope` are updated appropriately for your system. The other values should already be set in the configuration above.

Run the `orchestrator-install` playbook when the configuration file has been changed.
 
### Group definition files

In the configured `group_dir` directory, each file in this directory should match a group name that is defined in LDAP. Each file is in XML format, telling the sync script which Roles should be assigned to which Users. Each file should have an `.xml` suffix.

For example, you could have a file called `opsview-admin.xml` with the contents of:

  ```
  <contact>
  <name>%NAME%</name>
  <username>%SAMACCOUNTNAME%</username>
  <comment>%USERPRINCIPALNAME%</comment>
  <role><name>View some, change none</name></role>
  <variables><name>EMAIL</name><value>%USERPRINCIPALNAME%</value></variables>
  <variables><name>RSS_MAXIMUM_ITEMS</name><value>100</value></variables>
  <variables><name>RSS_MAXIMUM_AGE</name><value>2880</value></variables>
  <variables><name>RSS_COLLAPSED</name><value>1</value></variables>
  <variables><name>PAGER</name><value>%telephonenumber%</value></variables>
  </contact>
  ```

And a file called `yourldapgroup.xml`:

  ```
  <contact>
  <name>%NAME%</name>
  <username>%SAMACCOUNTNAME%</username>
  <comment>%USERPRINCIPALNAME%</comment>
  <role><name>View all, change none</name></role>
  <variables><name>EMAIL</name><value>%USERPRINCIPALNAME%</value></variables>
  <variables><name>RSS_MAXIMUM_ITEMS</name><value>100</value></variables>
  <variables><name>RSS_MAXIMUM_AGE</name><value>2880</value></variables>
  <variables><name>RSS_COLLAPSED</name><value>1</value></variables>
  <enable_tips>0</enable_tips>
  <language>de</language>
  <sharednotificationprofiles><name>Receive all alerts during work hours</name></sharednotificationprofiles>
  </contact>
  ```

This will search for the groups called `opsview-admin` and `yourldapgroup` in LDAP using the `group_filter` specified in the configuration. All members of this group will then have an Opsview configuration created with the fields defined in the XML. Any fields that are not defined in the XML will be inserted with default values.

Within the XML, you can use macros, which have the format `%MACRONAME%`. The macro name will be expanded with the LDAP attributes returned. You can run `opsview_sync_ldap -a` to list all the attributes returned from LDAP.


**Note**


If the the script is run with the `-C` option, then the `group_dir` configuration can be set as a relative path to the base directory given by `-C`.



### Multiple groups

By default, `opsview_sync_ldap` will only process one group definition file per User. If a User belongs to more than one LDAP group that has a matching group definition, then the User will be created with the information from the first matching group definition found (group definition files are processed in alphabetical order). A warning will be displayed that the other matching group definitions are ignored.


**Warning**


With the `-M` option, the script can be made to process multiple group definitions per User. The script then creates new combined roles that have as many access rights as all of the matching group definitions' roles combined or more. Because of that last caveat, the `-M` option is not recommended for normal use cases. Please contact the [ITRS Support Team](https://support.itrsgroup.com/hc/en-us/articles/115004001525-How-to-use-and-navigate-the-new-ITRS-Support-Portal-as-well-as-raise-tickets) if you must use this functionality.



### Nested groups

If your groups contain sub-groups, then they will be searched for Users. Users found in sub-groups will be considered group members just as if they were found directly within the parent group.

If a sub-group is also configured with its own group definition file, then Users of that sub-group will be considered members of both the sub-group and the parent group. This is then handled as described under [Multiple Groups](#multiple-groups).

## Monitoring of synchronization script

Opsview will begin to monitor the synchronization script status once you have declared an LDAP realm with `opsview_sync` settings defined, and run the `setup-monitoring.yml` deploy playbook:

```
/opt/opsview/deploy/bin/opsview-deploy /opt/opsview/deploy/lib/playbooks/setup-monitoring.yml
```

The **Opsview - LDAP Sync Status** service check will be added to your self-monitoring Host.

By default, this check will return a `WARNING` status if the synchronization script has not run successfully in the last 12 hours, or a `CRITICAL` status if the script has not run successfully for 24 hours or has never run. You can adjust the warning and critical thresholds of the service check to suit your expected schedule.

To resolve the initial `CRITICAL` status of the **Opsview - LDAP Sync Status** service check, due to the synchronization script not having run yet, you can run it manually:

  ```
  sudo -u opsview /opt/opsview/webapp/bin/opsview_sync_ldap -y
  ```

## Troubleshooting

If you get the error message `Authentication error: contact administrator` on the login page, check the audit log entries, as this will show the specific authentication failure. These failures may be problems such as the LDAP server being unavailable or not being possible to bind with the credentials specified in the configuration file. The error will also be seen in the syslog.

If you need to navigate your LDAP directory and see the structure, you may wish to use a product such as [Apache Directory Studio](https://directory.apache.org/studio/) which provides this functionality.

### Testing LDAP connectivity

Use `opsview_sync_ldap` with `-t` to test connectivity to LDAP, using the configured bind credentials:

  ```
  sudo -u opsview /opt/opsview/webapp/bin/opsview_sync_ldap -t
  ```

You can also enter a username and password to see if the credentials will work correctly and allow a user to log in:

  ```
  sudo -u opsview /opt/opsview/webapp/bin/opsview_sync_ldap -t -u {username} -p {password}
  ```

This uses the same routines that Opsview Monitor would use, so if it works here then authentication via Opsview Monitor should work too. Remember, if you make changes to `opsview_web_local.yml`, you must restart opsview-web for these to take effect in the web front end.

Finally, to add all users into Opsview run:

  ```
  sudo -u opsview /opt/opsview/webapp/bin/opsview_sync_ldap -y
  ```

Add this script to crontab if you want this to be regularly run.

### Testing LDAP user access

Examples of testing LDAP user access with the OpenLDAP `ldapsearch` tool:

  ```
  ldapsearch -x -h <ldap_host> -D <bind_dn> -W "objectclass=account" -b <search_base> cn uid homeDirectory

  ldapsearch -x -h opsview.example.net -D "SVC-OPSVIEW-LDAP" -W -b "OU=Opsview,OU=Application Servers,DC=opsview,DC=example,DC=net" search=(&(objectClass=member)

  ldapsearch -x -h opsview.example.net -D "SVC-OPSVIEW-LDAP" -W -b "OU=Opsview,OU=Application Servers,DC=opsview,DC=example,DC=net" search=(&(objectClass=member) attrs=sAMAccountName
  ```

### Opsview-web will not restart

If you get the error:

  ```
  Cannot find version string in opsview_web.yml - file is possibly invalid (remove any hardtabs)
  ```

Then you have probably got tabs in the `opsview_web_local.yml` file. YML requires spaces only; remove tabs from the configuration file.

### XML invalid

Your XML is likely to be incorrect if you get an error similar to:

  ```
  End tag mismatch (contact != notification_period) [Ln: 21, Col: 9]
  ```

Check that the XML is valid.

### Problem logging in via web interface

Verify if you have multiple domain controllers and make sure they can all be reached from the Opsview Master server if it takes several tries to log in to the web interface.


