Support for Multiple Active Directory Domains

I have ODFE clusters setup to support single AD domains.
I’m now in a position to have to support multiple AD domains where the user ids (firstname.lastname) may be duplicated.

I’ve tried setting up multiple authc/authz providers in the config as outlined in the following post (which will link to a SearchGuard forum topic):

This works OK if the login ids are unique across the multiple domains.

What I cannot figure out is how to allow users with accounts in both AD domains to explicitly specify which account they will be logging in with. For example:

corp\jaimie.livingston (or jaimie.livingston@corp) and dev\jaimie.livingston (or jaimie.livingston@dev)

Is there a config example for setting up multiple authc/authz to allow specifying the OU or Domain as part of the users login?

Thanks,

Jaimie Livingston

@jaimie.livingston What version of odfe do you use? What do you use as IDP?
Could you share your config.yml?

@pablo
Apologies for the delay. I was on an extended holiday.

The version I’m currently running is 1.11.0 (ElasticSearch and Kibana 7.9.1).
Below is the current config.yml, with some obfuscation for passwords and the like.
Need/Goal is to be able to support two separate AC Domains, so that users can log in as either

mycompany\user.name
-or-
othercompany\user.name

I’m only showing the authc and authz for the one mycompany domain, I can and have loaded separate authc and authz sections for othercompany in addition, but I could not sort out how to get the system to differentiate between mycompany\user.name and othercompany\user.name. ODFE seems to attempt load the first match for user.name, without any regard to the ad domain (mycompany\ or othercompany).

Thanks for any time spent on this. It is appreciated.

#config.yml
---
# The --- is require to be the first line of a YAML file...
#
# This is the main Open Distro Security configuration file where authentication
# and authorization is defined.
#

_meta:
  type: "config"
  config_version: 2

config:

  dynamic:
    do_not_fail_on_forbidden: true

    http:
      anonymous_auth_enabled: false
      xff:
        enabled: false
        internalProxies: '192\.168\.0\.10|192\.168\.0\.11' # regex pattern

    authc:

      internal_users_domain:
        description: "ElasticSearch Internal Users Database"
        http_enabled: true
        transport_enabled: true
        order: 1
        http_authenticator:
          type: basic
          challenge: true
        authentication_backend:
          type: intern

      mycompany_local_ad_domain:
        description: "MyCompany Active Directory"
        http_enabled: true
        transport_enabled: true
        order: 2
        http_authenticator:
          type: basic
          challenge: true
        authentication_backend:
          # LDAP authentication backend (authenticate users against a LDAP or Active Directory)
          type: ldap
          config:
            # enable ldaps
            enable_ssl: true
            # enable start tls, enable_ssl should be false
            enable_start_tls: false
            # send client certificate
            enable_ssl_client_auth: false
            # verify ldap hostname
            verify_hostnames: false
            hosts:
            - mcwindc01.mycompany.local:636
            - mcwindc02.mycompany.local:636
            #connect_timeout in milliseconds. How long does it take the server to respond
            connect_timeout: 3000
            #response_timeout in milliseconds. If 2FA is used, set to to value that gives users enough time to respond
            response_timeout: 30000
            bind_dn: 'CN=odfesvc,OU=Service Accounts,OU=Users and Groups,DC=mycompany,DC=local'
            password: NotTheRealPassword
            userbase: 'OU=Users and Groups,DC=mycompany,DC=local'
            # Filter to search for users (currently in the whole subtree beneath userbase)
            # {0} is substituted with the username
            usersearch: '(sAMAccountName={0})'
            # Use this attribute from the user as username (if not set then DN is used)
            username_attribute: sAMAccountName


    authz:

      mycompany_local_ad:
        description: "MyCompany AD Authorization Sources"
        http_enabled: true
        transport_enabled: true
        authorization_backend:
          # LDAP authorization backend (gather roles from a LDAP or Active Directory, you have to configure the above LDAP authentication backend settings too)
          type: ldap
          config:
            # enable ldaps
            enable_ssl: true
            # enable start tls, enable_ssl should be false
            enable_start_tls: false
            # send client certificate
            enable_ssl_client_auth: false
            # verify ldap hostname
            verify_hostnames: false
            hosts:
            - mcwindc01.mycompany.local:636
            - mcwindc02.mycompany.local:636
            bind_dn: 'CN=odfesvc,OU=Service Accounts,OU=Users and Groups,DC=mycompany,DC=local'
            password: rA0AwF0AohH6a09PIQXd
            #Disable use of custom_attribute_names
            custom_attr_maxval_len: 1
            userbase: 'OU=Users and Groups,DC=mycompany,DC=local'
            # Filter to search for users (currently in the whole subtree beneath userbase)
            # {0} is substituted with the username
            #usersearch: '(uid={0})'
            usersearch: '(sAMAccountName={0})'
            # Filter to search for roles (currently in the whole subtree beneath rolebase)
            # {0} is substituted with the DN of the user
            # {1} is substituted with the username
            # {2} is substituted with an attribute value from user's directory entry, of the authenticated user. Use userroleattribute to specify the name of the attribute
            rolebase: 'OU=Users and Groups,DC=mycompany,DC=local'
            rolesearch: '(member={1})'
            # Specify the name of the attribute which value should be substituted with {2} above
            userroleattribute: null
            # Roles as an attribute of the user entry
            #userrolename: disabled
            userrolename: memberOf
            # The attribute in a role entry containing the name of that role, Default is "name".
            # Can also be "dn" to use the full DN as rolename.
            rolename: cn
            #rolename: name
            # Resolve nested roles transitive (roles which are members of other roles and so on ...)
            resolve_nested_roles: true
            # Skip users matching a user name, a wildcard or a regex pattern
            #skip_users:
            #  - 'cn=Michael Jackson,ou*people,o=TEST'
            #  - '/\S*/'
1 Like

Hi @jaimie.livingston

Apologies for the late response. Your issue is in the config of the usersearch and username_attribute.
“sAMAccountName” as you know is not unique if you handle more than one AD.
The solution is to ask for more unique attribute from AD.

Your current config is:

usersearch: ‘(sAMAccountName={0})’
username_attribute: sAMAccountName

instead try following:

usersearch: “(userPrincipalName={0})”
username_attribute: “userPrincipalName”

You will lose ability to use short username and will be forced to use UPN format i.e.

username@mycompany.local

Hope this will help.

@pablo

I couldn’t see the tree for the forest. :wink:
It’s so obvious when someone points it out.

Thank you so very much.

Jaimie

1 Like