0

My scenario is similar to what's described in this question How to authenticate users with a customer's (remote) active directory server, except that our web application is Java based and runs under CentOS Linux.

I have been searching and reading a lot, but don't seem to be able to find useful information for my scenario because of the following:

  1. Although there are different solutions for Java based application to authenticate against AD (Kerberos, NTLM, LDAP from Authenticating against Active Directory with Java on Linux), but I don't think they are for remote AD authentication from what I read.
  2. Kerberos is said to be the "new" mechanism for AD authentication recommended by Microsoft. However, it seems that either conf files or system properties are required for it to work. Our web application is SaaS based so the same code base will server multiple customers, and I don't see a good way to make Kerberos work nicely (even if it support remote authentication, which I don't see).
  3. Product Jespa uses NTLMv2 based authentication (again, I am not seeing it supports remote authentication), which is "old" and not recommended by Microsoft anymore. Plus it requires the creation of a "computer account".
  4. Okta has a solution that will work for the remote scenario I am facing. However, it requires the installation of an "AD agent" on customers AD server to handle all SAML communications between the "AD agent" and a Java library sitting with our web application. It's definitely a concern for our customers to install a software in their infrastructure that they are not a customer of.

Now based on my recent reading, best security practices are for organizations to hide Active Directory server behind firewall and do not expose it to the Internet. I wonder whether that means there will not be "direct" path of remote Active Directory authentication integration regardless of tools and libraries, and some kind of trusted agent or proxy residing within the customer infrastructure will be required to facilitate the remote authentication.

Any comments and suggestions are welcome!

Community
  • 1
  • 1
Haixi Liu
  • 43
  • 1
  • 2
  • 6
  • Which application server you are using to develop & deploy web application ? – OO7 Oct 15 '14 at 18:45
  • Yes. it is possible to access remote AD. But which mechanism do you want to use ? – OO7 Oct 15 '14 at 18:51
  • Local development uses embedded jetty in IntelliJ. Server is tomcat 7. – Haixi Liu Oct 15 '14 at 19:18
  • Have you tried configuration of LDAP in Tomcat ? Do you have Apache Directory installed on your machine ? – OO7 Oct 15 '14 at 19:21
  • No. From what I am reading, LDAP carries more than what I need (authentication only with active directory). – Haixi Liu Oct 15 '14 at 19:41
  • What does this mean *LDAP carries more than what I need (authentication only with active directory)* ? – OO7 Oct 15 '14 at 19:57
  • Try the Jespa example webapp as described in the Installation section in the Jespa Operator's Manual. Jespa almost certainly does what you seek, it is very simple, robust and popular. NTLMv2 is not as weak as people like to claim and it is in use more than people think because Kerberos has a number of requirements that can be difficult to satisfy / maintain. Note that any server-side authentication solution is going to require a service account in AD. – squarewav Nov 16 '14 at 23:37

1 Answers1

1

We can access remote AD via LDAP protocol. In my scenario, I have LDAP configured on remote server. I have installed Apache Active DS to create an instance of remote directory at my end. So I can have a configuration details at my end. To do this we require domain name & bind credentials. You can download Apache Directory for Linux from this link & create new connection. See LDAP Connections to Active Directory Using Apache Directory Studio

LDAP Configuration for Tomcat :

Sample JNDIRealm configuration for Tomcat 7 - Apache.org

Add your desired Realm configuration between Host opening and Host closing tag in server.xml file located at conf directory of Tomcat. e.g.,

<Host name="localhost"  appBase="webapps"
        unpackWARs="true" autoDeploy="true">
    <Realm   className="org.apache.catalina.realm.JNDIRealm"
        connectionName="cn=Manager,dc=mycompany,dc=com"
        connectionPassword="secret"
        connectionURL="ldap://localhost:389"
        userPassword="userPassword"
        userPattern="uid={0},ou=people,dc=mycompany,dc=com"
        roleBase="ou=groups,dc=mycompany,dc=com"
        roleName="cn"
        roleSearch="(uniqueMember={0})"
    />
    <!-- other stuffs -->
</Host>

Don't forget to comment out the below entry in server.xml file.

<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
           resourceName="UserDatabase"/>

Once you are done with this then it's time to configure your web application which will give a call to LDAP server via Tomcat server.

Web Application Configuration :

Add below entries in your web.xml file placed inside WebContent -> WEB-INF directory.

<security-constraint>
    <web-resource-collection>
          <web-resource-name>Logging Area</web-resource-name>
          <description>
              Authentication Required.
          </description>
          <url-pattern>/*</url-pattern>
          <http-method>GET</http-method>
          <http-method>POST</http-method>
    </web-resource-collection>
        <auth-constraint>
            <role-name>*</role-name>
        </auth-constraint>
    </security-constraint>
   <security-role>
      <role-name>*</role-name>
   </security-role>
    <login-config>
          <auth-method>BASIC</auth-method>
        <realm-name>Authentication Required.</realm-name>
</login-config>

This is the basic configuration. But, you can map specific user or group with LDAP. Have a look at Mapping Roles to Users and Groups - J2EE Tutorial. You can also add security to specific folder only, by adding url pattern for it under web-resource-collection tags.

<url-pattern>/Admin/*</url-pattern> // Restrict access to Admin folder
<url-pattern>/Employee/*</url-pattern>  // Restrict access to Employee folder

All configuration are done. Now, when your run you application it will ask for Login Credentials. Once you enter & hit submit it will search for that user in LDAP server.

Tomcat LDAP Authentication Window

At server, to get access to user name use below code snippet :-

Principal principal = request.getUserPrincipal();
String userName = principal.getName();

Example LDAP User Manager (JBOSS):

public LdapContext getLdapContext() {
    // Set up environment for creating initial context
    Hashtable<String, Object> env = new Hashtable<String, Object>(11);

    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    // e.g., ldap://IP address of remote m/c:10389/dc=sevenseas,dc=com
    env.put(Context.PROVIDER_URL, LDAP_PROVIDER_URL + Constant.FORWARD_SLASH + LDAP_DOMAIN);

    // Authenticate as User and password
    env.put(Context.SECURITY_AUTHENTICATION, "simple");
    // e.g., "uid=admin,ou=system"
    env.put(Context.SECURITY_PRINCIPAL, LDAP_ADMIN);
    env.put(Context.SECURITY_CREDENTIALS, LDAP_DEFAULT_PASSWORD);

    try {
        // Create initial context
        ldapContext = new InitialLdapContext(env, null);
        if (ldapContext == null) {
            LogManager.fatal("Invalid LDAP system properties. Please contact your administrator.",
                    LDAPUserManager.class.getName());
        }
        System.out.println("Organization : " + ldapContext.getNameInNamespace());
    } catch (Exception e) {
        StringWriter stack = new StringWriter();
        e.printStackTrace(new PrintWriter(stack));
        LogManager.fatal(stack.toString(), LDAPUserManager.class.getName());
    }
    return ldapContext;
}

This will Construct initial context with environment parameters to connect with LDAP server.

Now, to fetch user data from AD use the below code :

try {
        SearchControls constraints = new SearchControls();
        constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
        String[] attrIDs = { "cn", "sn", "givenName", "uid", "mail", "userPassword" };

        constraints.setReturningAttributes(attrIDs);

        // Search for user in LDAP server by emailId
        NamingEnumeration<SearchResult> answer = ldapContext.search(LDAP_GROUP, "mail=" + emailId, constraints);
        if (answer.hasMore()) {
            LogManager.info("User with email id '" + emailId + "' found.", LDAPUserManager.class.getName());

            Attributes attrs = answer.next().getAttributes();

            userDTO = new UserDTO();

            // Store user details temporarily
            Attribute firstNameAttr = attrs.get("givenName");
            String firstName = "";
            if (firstNameAttr == null) {
                userDTO.setFirstName(firstName);
            } else {
                firstName = firstNameAttr.get().toString();
                if (firstName == null) {
                    userDTO.setFirstName("");
                }
                userDTO.setFirstName(firstName);
            }

            Attribute lastNameAttr = attrs.get("sn");
            String lastName = "";
            if (lastNameAttr == null) {
                userDTO.setLastName(lastName);
            } else {
                lastName = lastNameAttr.get().toString();
                if (lastName == null) {
                    userDTO.setLastName("");
                }
                userDTO.setLastName(lastName);
            }

            return userDTO;
        } else {
            LogManager.fatal("Invalid User.", LDAPUserManager.class.getName());
            return userDTO;
        }
    } catch (Exception e) {
        StringWriter stack = new StringWriter();
        e.printStackTrace(new PrintWriter(stack));
        LogManager.fatal(stack.toString(), LDAPUserManager.class.getName());
    }

I hope this helps you.

OO7
  • 2,785
  • 1
  • 21
  • 33
  • thanks for the answer. I see two challenges in making this scenario work for us: 1. How can we handle multiple remote ADs by having a consolidated local LDAP - we are SaaS, so there may be more than one remote AD that needs to be integrated on one web app server; or some of our customers need remote AD, and some uses normal Database authentication (we use different URL pattern to tell users from one customer apart from another customer). 2. How is changes in remote AD get synchronized to local LDAP, is that automatic? – Haixi Liu Oct 16 '14 at 10:25
  • Yes, synchronization part is done automatically. But, to see those changes in local environment you to need to restart your LDAP server created in Apache Directory. Regarding Q1, we can create multiple LDAP server in Apache Directory, each having different domain name. You can access them programmatically. I didn't did this type of configuration in Tomcat but, you can refer JBoss example. See my updated answer. If I find way on this, I'll update my answer with Tomcat Configuration. Till then try with JBoss example code. – OO7 Oct 16 '14 at 13:53
  • @ Haixi Liu Have you tried above code ? Will it help you ? – OO7 Oct 16 '14 at 18:04
  • @ OO7, I have not tried. After discussing the scenario and your suggestion internally, our team is leaning toward requiring our partners (customers) to allow us access to their AD via VPN tunnel, so we can program as if we are authenticating against a local AD in our own network. The one thing we don't like is having to restart local LDAP server for changes since we expect frequent changes from customers' AD server. Thanks a lot for the answer. – Haixi Liu Oct 16 '14 at 19:45
  • @ Haixi Liu Sorry about `restarting local LDAP server`. I went through comments & found *mistake in my comment*. You have 2 **create only connection** at ur end, **not a server**. If there r any changes in remote AD, they r reflected immediately. You just have to *restart local connection*. It's not a big deal. And **connection is to see entries present in remote AD at ur end**. It doesn't have any impact on coding part or server configuration part. Even *no need 2 create connection locally, if u have domain details then just configure it in Tomcat & start coding*. – OO7 Oct 17 '14 at 05:21
  • @ Haixi Liu Have you tried this solution ? Is this works for you ? What is the response from your team ? Do you get any other solution for this ? I am interested to learn new solution, if find better than this. – OO7 Oct 20 '14 at 18:15