---
title: 'Tutorial: Configure LDAP Filters in Access Server to Restrict VPN Access by Active Directory Group'
canonical: https://openvpn.net/as-docs/v3/tutorials/tutorial--ldap-filter-active-directory-group/
product: Access Server Docs
description: Configure LDAP filters in Access Server to restrict VPN authentication by Active Directory group — covers single group, multiple groups, nested groups using LDAP_MATCHING_RULE_IN_CHAIN, and allowing one nested group while denying another, with Admin Web UI and CLI steps.
---

# Tutorial: Configure LDAP Filters in Access Server to Restrict VPN Access by Active Directory Group

## Overview

OpenVPN Access Server supports LDAP filter syntax to control which users can authenticate through LDAP. You can use LDAP filters to restrict VPN access to members of specific Microsoft Active Directory (AD) groups.

This tutorial covers several common group-membership scenarios using the Active Directory `memberOf` attribute:

- Allow members of one specific group.
- Allow members of multiple groups.
- Allow direct and indirect members of a parent group through nested groups.
- Allow members of selected nested groups while excluding others.

The examples use the Base DN, which defines the starting point in the LDAP directory tree where Access Server begins searching for users and groups.

For nested group membership, the tutorial uses LDAP\_MATCHING\_RULE\_IN\_CHAIN, identified by OID `1.2.840.113556.1.4.1941`. This Active Directory matching rule enables recursive nested-group membership lookups without requiring multiple LDAP queries from the application.

You'll also use Access Server's `authcli` tool to test LDAP authentication directly from the command line without establishing a VPN connection.

Group-based LDAP filters can also support least-privilege access policies used in [Zero Trust Network Access](https://openvpn.net/use-cases/zero-trust-network-access/) deployments.

## Prerequisites

Before you begin, ensure you have:

- An installed Access Server with LDAP authentication configured. If LDAP isn't configured yet, complete [Tutorial: Set Up Access Server with Active Directory via LDAP for VPN Integration](https://openvpn.net/as-docs/v3/tutorials/tutorial--active-directory-ldap.md).
- Admin Web UI access or console access with root privileges.
- A Windows Active Directory server with LDAP configured.
- Existing AD users and groups you can use to test the filters.

### Example environment

The examples in this tutorial use these values:

| **Setting** | **Example value** |
| --- | --- |
| Parent group | `Group0` |
| Child group 1 | `Group1` |
| Child group 2 | `Group2` |
| Child group 3 | `Group3` |
| LDAP user 1 | `brandonuser1` |
| LDAP user 2 | `brandonuser2` |
| LDAP user 3 | `brandonuser3` |
| Base DN | `CN=Users,DC=brandonldap,DC=net` |

Replace these values with the corresponding values from your environment.

### Placeholder values

The commands in this tutorial use the following placeholders:

| **Placeholder** | **Description** |
| --- | --- |
| `<BASE_DN>` | The starting point in the **LDAP directory tree** where searches begin. |
| `<FULL_DN>` | The distinguished name (DN) that uniquely identifies a specific LDAP user or group. |
| `<PASSWORD>` | The LDAP user's password. |

> **Note**
>
> In our documentation, we use example IPv4 addresses and subnets [reserved for documentation](https://en.wikipedia.org/wiki/Reserved_IP_addresses#IPv4), such as `192.0.2.0/24`, `198.51.100.0/24`, and `203.0.113.0/24`.
>
> Ensure you replace them with valid IPv4 addresses and subnets for your network(s).

## Step 1: Filter by memberOf for one Active Directory group

Use this scenario when only members of one specific AD group should be allowed to authenticate.

In this example,

- `brandonuser1` is a member of `Group1`.
- `brandonuser2` is a member of `Group2`.
- Only members of `Group1` should be allowed to authenticate.

### Verify the user and group with LDAP Explorer

Before applying the LDAP filter, confirm that Access Server can locate the users and groups in Active Directory.

1. Connect to the console and get root privileges.
2. Verify that Access Server can locate `brandonuser1`:

   ```
   sacli --dn='CN=Users,DC=brandonldap,DC=net' --scope 'subnode' --key 'sAMAccountName' --value 'brandonuser1' ldapexp
   ```

   - Example output:

     ```
     {
       "reason": "LDAP query succeeded on ldap://192.0.2.10",
       "result": [
         "CN=brandonuser1,CN=Users,DC=brandonldap,DC=net"
       ],
       "status": 0
     }
     ```
3. Verify that Access Server can locate `Group1`:

   ```
   sacli --dn='CN=Users,DC=brandonldap,DC=net' --scope 'subnode' --key 'sAMAccountName' --value 'Group1' ldapexp
   ```

   - Example output:

     ```
     {
       "reason": "LDAP query succeeded on ldap://192.0.2.10",
       "result": [
         "CN=Group1,CN=Users,DC=brandonldap,DC=net"
       ],
       "status": 0
     }
     ```
4. Verify the users that have `Group1` in their `memberOf` attribute:

   ```
   sacli --dn 'CN=Users,DC=brandonldap,DC=net' --scope 'subnode' --key memberOf --value 'CN=Group1,CN=Users,DC=brandonldap,DC=net' ldapexp
   ```

   - Example output:

     ```
     {
       "reason": "LDAP query succeeded on ldap://192.0.2.10",
       "result": [
         "CN=brandonuser1,CN=Users,DC=brandonldap,DC=net"
       ],
       "status": 0
     }
     ```
5. Repeat these checks for any other users or groups you plan to test.

### Configure the memberOf LDAP filter in the Admin Web UI

1. Sign in to the Admin Web UI.
2. Navigate to **Authentication → LDAP**.
3. Under **LDAP filter**, enter:

   ```
   memberOf=<FULL_DN>
   ```

   - For this example:

     ```
     memberOf=CN=Group1,CN=Users,DC=brandonldap,DC=net
     ```
4. Select **Save** and **Restart**.

### Configure the memberOf LDAP filter with sacli

You can also configure the filter from the command line with the [sacli command-line tool](https://openvpn.net/as-docs/v3/tutorials/tutorial--sacli-command-line-tool.md).

1. Connect to the console and get root privileges.
2. Configure the LDAP filter:

   ```
   sacli --key "auth.ldap.0.add_req" --value "memberOf=<FULL_DN>" ConfigPut
   ```

   - For this example:

     ```
     sacli --key "auth.ldap.0.add_req" --value "memberOf=CN=Group1,CN=Users,DC=brandonldap,DC=net" ConfigPut
     ```
3. Restart the Access Server services:

   ```
   sacli start
   ```

### Verify the memberOf LDAP filter with authcli

Access Server's `authcli` tool lets you test LDAP authentication without first establishing a VPN connection.

1. Verify that `brandonuser1`, a member of `Group1`, can authenticate:

   ```
   /usr/local/openvpn_as/scripts/authcli --user brandonuser1 --pass <PASSWORD>
   ```

   - Example output:

     ```
     AUTH_RETURN
       status : SUCCEED
       user : brandonuser1
       reason : LDAP auth succeeded on ldap://192.0.2.10
       auth method : ldap
       proplist : {'prop_autogenerate': 'true', 'type': 'user_connect', 'pvt_epoch': '17746636625031316972'}
       session_id : AS_H403XkX7Aexthq5LfecgPA==
       expire : 1787068752
     ```
2. Verify that `brandonuser2`, who isn't a member of `Group1`, can't authenticate:

   ```
   /usr/local/openvpn_as/scripts/authcli --user brandonuser2 --pass <PASSWORD>
   ```

   - Example output:

     ```
     API METHOD: authenticate
     AUTH_RETURN
       status : FAIL
       user : brandonuser2
       reason : user not found that meets specified requirements: memberOf=CN=Group1,CN=Users,DC=brandonldap,DC=net
       auth method : ldap
     ```
3. After verifying the filter with `authcli`, test authentication through a VPN connection.

## Step 2: Filter by memberOf for multiple Active Directory groups

Use this scenario when users from more than one AD group should be allowed to authenticate.

In this example:

- `brandonuser1` is a member of `Group1`.
- `brandonuser2` is a member of `Group2`.
- `brandonuser3` is a member of `Group3`.
- Members of `Group1` and `Group2` should be allowed.
- Members of `Group3` should be denied.

### Verify the users and memberOf group membership

Use LDAP Explorer as described in Step 1 to confirm that Access Server can locate:

- `brandonuser1`
- `brandonuser2`
- `brandonuser3`
- `Group1`
- `Group2`
- `Group3`

1. Verify that Access Server can locate the necessary users and groups. Examples:

   ```
   sacli --dn='CN=Users,DC=brandonldap,DC=net' --scope 'subnode' --key 'sAMAccountName' --value 'brandonuser1' ldapexp
   ```

   ```
   sacli --dn='CN=Users,DC=brandonldap,DC=net' --scope 'subnode' --key 'sAMAccountName' --value 'Group1' ldapexp
   ```

   ```
   sacli --dn 'CN=Users,DC=brandonldap,DC=net' --scope 'subnode' --key memberOf --value 'CN=Group1,CN=Users,DC=brandonldap,DC=net' ldapexp
   ```
2. Confirm with each user and group.

### Configure the multi-group memberOf filter in the Admin Web UI

1. Sign in to the Admin Web UI.
2. Navigate to **Authentication → LDAP**.
3. Under **LDAP filter**, enter

   ```
   |(<FULL_DN>)(<FULL_DN>)
   ```

   > **Important**
   >
   > Include the leading pipe (`|`). It specifies the OR condition used by this Access Server filter.

   - For this example:

     ```
     |(memberOf=CN=Group1,CN=Users,DC=brandonldap,DC=net)(memberOf=CN=Group2,CN=Users,DC=brandonldap,DC=net)
     ```
4. Select **Save** and **Restart**.

### Configure the multi-group memberOf filter with sacli

1. Connect to the console and get root privileges.
2. Configure the LDAP filter:

   ```
   sacli --key "auth.ldap.0.add_req" --value "|(<FULL_DN>)(<FULL_DN>)" ConfigPut
   ```
3. Restart Access Server services:

   ```
   sacli start
   ```

### Verify the multiple-group memberOf filter with authcli

1. Test all three users:
   - Expected: `SUCCEED`

     ```
     /usr/local/openvpn_as/scripts/authcli --user brandonuser1 --pass <PASSWORD>
     ```
   - Expected: `SUCCEED`

     ```
     /usr/local/openvpn_as/scripts/authcli --user brandonuser2 --pass <PASSWORD>
     ```
   - Expected: `FAIL`

     ```
     /usr/local/openvpn_as/scripts/authcli --user brandonuser3 --pass <PASSWORD>
     ```
2. After verifying the expected results, test authentication through a VPN connection.

## Step 3: Filter by nested group membership with memberOf

Use this scenario when users belong to nested groups and you want to allow both direct and indirect members of a parent group.

In this example:

- `Group0` is the parent group.
- `Group1` and `Group2` are child groups nested under `Group0`.
- `brandonuser1` is a direct member of `Group1`.
- `brandonuser2` is a direct member of `Group2`.
- Both users are therefore indirect members of `Group0`.

A standard `memberOf=...` filter only evaluates direct group membership. For recursive nested-group lookups in Active Directory, use `LDAP_MATCHING_RULE_IN_CHAIN`.

`LDAP_MATCHING_RULE_IN_CHAIN`, identified by OID `1.2.840.113556.1.4.1941`, allows Active Directory to evaluate the membership chain and find users who belong to the parent group indirectly through nested child groups.

### Verify the nested groups with LDAP Explorer

1. Verify that Access Server can locate `brandonuser1` and `Group1`:
   - `brandonuser1`:

     ```
     sacli --dn='CN=Users,DC=brandonldap,DC=net' --scope 'subnode' --key 'sAMAccountName' --value 'brandonuser1' ldapexp
     ```
   - Example output:

     ```
     {
       "reason": "LDAP query succeeded on ldap://192.0.2.10",
       "result": [
         "CN=brandonuser1,CN=Users,DC=brandonldap,DC=net"
       ],
       "status": 0
     }
     ```
   - `Group1`:

     ```
     sacli --dn='CN=Users,DC=brandonldap,DC=net' --scope 'subnode' --key 'sAMAccountName' --value 'Group1' ldapexp
     ```
   - Example output:

     ```
     {
       "reason": "LDAP query succeeded on ldap://192.0.2.10",
       "result": [
         "CN=Group1,CN=Users,DC=brandonldap,DC=net"
       ],
       "status": 0
     }
     ```
2. Verify that Access Server can locate members of `Group1`:

   ```
   sacli --dn 'CN=Users,DC=brandonldap,DC=net' --scope 'subnode' --key memberOf --value 'CN=Group1,CN=Users,DC=brandonldap,DC=net' ldapexp
   ```

   - Example output:

     ```
     {
       "reason": "LDAP query succeeded on ldap://192.0.2.10",
       "result": [
         "CN=brandonuser1,CN=Users,DC=brandonldap,DC=net"
       ],
       "status": 0
     }
     ```
3. Verify that `Group1` and `Group2` are members of `Group0`:

   ```
   sacli --dn 'CN=Users,DC=brandonldap,DC=net' --scope 'subnode' --key memberOf --value 'CN=Group0,CN=Users,DC=brandonldap,DC=net' ldapexp
   ```

   - Example output:

     ```
     {
       "reason": "LDAP query succeeded on ldap://192.0.2.10",
       "result": [
         "CN=Group1,CN=Users,DC=brandonldap,DC=net",
         "CN=Group2,CN=Users,DC=brandonldap,DC=net"
       ],
       "status": 0
     }
     ```
4. Confirm with each user and group.

### Configure nested memberOf with LDAP\_MATCHING\_RULE\_IN\_CHAIN in the Admin Web UI

1. Sign in to the Admin Web UI.
2. Navigate to **Authentication → LDAP**.
3. Under **LDAP filter**, enter:

   ```
   memberOf:1.2.840.113556.1.4.1941:=<FULL_DN>
   ```
4. Replace `<FULL_DN>` with the full DN of the parent group. For example:

   ```
   memberOf:1.2.840.113556.1.4.1941:=CN=Group0,CN=Users,DC=brandonldap,DC=net
   ```
5. Select **Save** and **Restart**.

### Configure nested memberOf with sacli

1. Connect to the console and get root privileges.
2. Configure the filter:

   ```
   sacli --key "auth.ldap.0.add_req" --value "memberOf:1.2.840.113556.1.4.1941:=<FULL_DN>" ConfigPut
   ```
3. Replace `<FULL_DN>` with the full DN of the parent group. For example:

   ```
   sacli --key "auth.ldap.0.add_req" --value "memberOf:1.2.840.113556.1.4.1941:=CN=Group0,CN=Users,DC=brandonldap,DC=net" ConfigPut
   ```
4. Restart the Access Server services:

   ```
   sacli start
   ```

### Verify nested group membership with authcli

1. Verify that users in either nested child group can authenticate:
   - Expected: `SUCCEED`

     ```
     /usr/local/openvpn_as/scripts/authcli --user brandonuser1 --pass <PASSWORD>
     ```
   - Expected: `SUCCEED`

     ```
     /usr/local/openvpn_as/scripts/authcli --user brandonuser2 --pass <PASSWORD>
     ```
2. Test a user who isn't a direct or indirect member of `Group0` (for example, `brandonuser3`):
   - Expected: `FAIL`

     ```
     /usr/local/openvpn_as/scripts/authcli --user brandonuser3 --pass <PASSWORD>
     ```
3. After verifying the expected results, test authentication through a VPN connection.

## Step 4: Filter nested groups with memberOf and exclude a child group

Use this scenario when users belong to the same parent group through different nested groups, but only selected child groups should be allowed to authenticate.

In this example:

- `Group0` is the parent group.
- `Group1` and `Group2` are nested child groups.
- `brandonuser1` and `brandonuser2` belong to `Group1`.
- `brandonuser3` belongs to `Group2`.
- Both child groups belong to `Group0`.
- Members of `Group1` should be allowed.
- Members of `Group2` should be denied.

This filter combines `LDAP_MATCHING_RULE_IN_CHAIN` with a negative `memberOf` condition.

### Verify the nested memberOf relationships

Use LDAP Explorer to verify:

- `Group1` and `Group2` are members of `Group0`.
- `brandonuser1` and `brandonuser2` are members of `Group1`.
- `brandonuser3` is a member of `Group2`.

1. Verify Access Server can see `brandonuser1`:

   ```
   sacli --dn='CN=Users,DC=brandonldap,DC=net' --scope 'subnode' --key 'sAMAccountName' --value 'brandonuser1' ldapexp
   ```

   - Example output:

     ```
     {
       "reason": "LDAP query succeeded on ldap://192.0.2.10",
       "result": [
         "CN=brandonuser1,CN=Users,DC=brandonldap,DC=net"
       ],
       "status": 0
     }
     ```
2. Verify Access Server can see `Group1`:

   ```
   sacli --dn='CN=Users,DC=brandonldap,DC=net' --scope 'subnode' --key 'sAMAccountName' --value 'Group1' ldapexp
   ```

   - Example output:

     ```
     {
       "reason": "LDAP query succeeded on ldap://192.0.2.10",
       "result": [
         "CN=Group1,CN=Users,DC=brandonldap,DC=net"
       ],
       "status": 0
     }
     ```
3. Verify Access Server can see `Group1` members:

   ```
   sacli --dn 'CN=Users,DC=brandonldap,DC=net' --scope 'subnode' --key memberOf --value 'CN=Group1,CN=Users,DC=brandonldap,DC=net' ldapexp
   ```

   - Example output:

     ```
     {
       "reason": "LDAP query succeeded on ldap://192.0.2.10",
       "result": [
         "CN=brandonuser1,CN=Users,DC=brandonldap,DC=net"
       ],
       "status": 0
     }
     ```
4. Verify Access Server can see `Group0` members:

   ```
   sacli --dn 'CN=Users,DC=brandonldap,DC=net' --scope 'subnode' --key memberOf --value 'CN=Group0,CN=Users,DC=brandonldap,DC=net' ldapexp
   ```

   - Example output:

     ```
     {
       "reason": "LDAP query succeeded on ldap://192.0.2.10",
       "result": [
         "CN=Group1,CN=Users,DC=brandonldap,DC=net",
         "CN=Group2,CN=Users,DC=brandonldap,DC=net"
       ],
       "status": 0
     }
     ```
5. Repeat with other users and groups.

### Configure the nested-group memberOf filter in the Admin Web UI

1. Sign in to the Admin Web UI.
2. Navigate to **Authentication → LDAP**.
3. Under **LDAP filter**, enter:

   ```
   |(&(memberOf:1.2.840.113556.1.4.1941:=<FULL_DN>)(!(memberOf:1.2.840.113556.1.4.1941:=<FULL_DN>)))
   ```

   > **Important**
   >
   > Include the leading pipe (`|`).
4. Use the parent group for the first DN and the excluded child group for the second. For this example:

   ```
   |(&(memberOf:1.2.840.113556.1.4.1941:=CN=Group0,CN=Users,DC=brandonldap,DC=net)(!(memberOf:1.2.840.113556.1.4.1941:=CN=Group2,CN=Users,DC=brandonldap,DC=net)))
   ```
5. Select **Save** and **Restart**.

### Configure the nested-group memberOf filter with sacli

1. Connect to the console and get root privileges.
2. Configure the filter:

   ```
   sacli --key "auth.ldap.0.add_req" --value "|(&(memberOf:1.2.840.113556.1.4.1941:=<FULL_DN>)(!(memberOf:1.2.840.113556.1.4.1941:=<FULL_DN>)))" ConfigPut
   ```

   > **Important**
   >
   > Include the leading pipe (`|`).
3. Restart the Access Server services:

   ```
   sacli start
   ```

### Verify the nested-group memberOf filter with authcli

Verify the expected behavior:

| **User** | **Membership** | **Expected result** |
| --- | --- | --- |
| `brandonuser1` | `Group1` → `Group0` | `SUCCEED` |
| `brandonuser2` | `Group1` → `Group0` | `SUCCEED` |
| `brandonuser3` | `Group2` → `Group0` | `FAIL` |

1. Test each user with:

   ```
   /usr/local/openvpn_as/scripts/authcli --user <USERNAME> --pass <PASSWORD>
   ```
2. After confirming the expected results with `authcli`, test authentication through a VPN connection.

## See also

- [Tutorial: Set Up Access Server with Active Directory via LDAP for VPN Integration](https://openvpn.net/as-docs/v3/tutorials/tutorial--active-directory-ldap.md)
- [Tutorial: Manage the LDAP Authentication Method from the Command-Line Interface](https://openvpn.net/as-docs/v3/tutorials/tutorial--ldap-command-line.md)
- [Tutorial: An Intro to the sacli Command-Line Utility](https://openvpn.net/as-docs/v3/tutorials/tutorial--sacli-command-line-tool.md)
- [Zero Trust Network Access](https://openvpn.net/use-cases/zero-trust-network-access/)

---

Prev: [Tutorial: Set Up Access Server with Active Directory via LDAP for VPN Integration](https://openvpn.net/as-docs/v3/tutorials/tutorial--active-directory-ldap.md)
Next: [Tutorial: Manage the LDAP Authentication Method from the Command-Line Interface](https://openvpn.net/as-docs/v3/tutorials/tutorial--ldap-command-line.md)
Index: https://openvpn.net/as-docs/llms.txt
