Active Directory Programming with Python – Flask API
Active Directory Programming with Python – Flask API
Introduction
In today’s world Active Directory is widely used in the IT industry for binding together people with resources like computers in an organization, in a secure way. In this document, I’m going to elaborate my experiments with Active Directory, from a programmer and Active Directory Administrator’s standpoint. I played the role of Active Directory Administrator while performing numerous setups for testing the APIs I wrote for accessing Active Directory.
Objective
Active Directory is a Microsoft product that is typically accessed and programmed using Microsoft technologies. However, we wanted to connect to and query the Active Directory using open source technologies like Python – Flask API on Ubuntu 18.04. This was a little unusual way of development, and hence more interesting!
For testing the work, we decided to go with AWS instances – the cloud proves to be the most economical and the least time-consuming choice when the requirement is make-test-and-break kind of setups. As you go ahead and read, you can see the value in this decision. We did multiple setups for testing which you will see as you read further.
Active Directory Topology
Active Directory essentially helps in managing resources of an organization, like users, groups, computers, devices, conference rooms, etc.
Active Directory is an abstraction of the organization structure and hierarchy. It has a root domain and subdomains if required. A domain is further divided into Organizational Units (OU), which maps to the organizational structure. An organization may have geographically separate offices. Each branch office requires its own domain controller for authentication.
The following diagram depicts a typical Active Directory topology along with typical resources that are managed by the Active Directory.
Active Directory APIs
We needed APIs to extract the following information from Active Directory.
1) Group Information:
Group information consists of the group name, group members, group membership, category of the group, etc.
2) User Information:
User information consists of user name, user membership, email, category of user, etc.
3) Authentication API:
This API authenticates the user and returns the authentication response.
Libraries used to build API
1) Flask:
Flask is very simple and lightweight as compared to the other python frameworks like Django REST framework. Unlike Django, Flask provides you the control over the components like which database to use if it is needed.
Flask gives URL routing, request and error handling, templating, cookies, support for unit testing, a debugger and a development server out-of-the-box. This is sufficient for a web service, and for the rest of the requirements, more libraries and frameworks can be chosen. As a result, the skeleton of a web service is ready while the application designer has the full flexibility of choosing other components for ORM, authentication, UI as required.
Django, on the other hand, believes in a ‘batteries included’ approach, which in turn, makes decisions about the other components upfront and imposes on the designer.
For the current requirements, we went for Flask since many of the components from the Django bundle like ORM and Auth were not necessary. The APIs needed to connect to an Active Directory and not to a database and for authentication as well the Active Directory itself as needed.
2) OpenAPI (Formerly Swagger)
OpenAPI provides an easy interface to API services. This helps in testing the APIs using any browser, and no special client tools are required. This interface can be used by the administrators for real life use cases as well.
Installation:
$ pip install Flask
$ pip install connexion
3) Python-Ldap
Python-ldap is the module which provides an object-oriented API to access Active Directory servers from Python programs. Python-ldap is an open source library and licenced under Python Software Foundation License (Python style) whose source code is also available on Git.
Installation:
$ python -m pip install python-ldap
Sample connection and authentication using Python-ldap.
conn = ldap.initialize('ldap://' + BIND_ADDRESS:389) conn.simple_bind_s(USERNAME, PASSWORD)
For secure connection I have used python-ldaps with valid certificates.
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND) ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, Constants.CACERTFILE) conn = ldap.initialize('ldaps://' + BIND_ADDRESS:636) conn.simple_bind_s(USERNAME, PASSWORD)
Sample group filter
"(&(objectClass=Group)(cn={<GROUP_NAME>}))"
Sample user filter
"(&(objectCategory=User)(cn={<USER_NAME>}))"
Testing Challenges
Testing the APIs was quite challenging, for the following reasons:
- The ‘real’ setup details where the APIs were to be used, were not known.
- Active Directory has a lot of features and different kinds of topologies, which gives rise to numerous setup requirements.
- Setting up all these on real hardware would be cumbersome, time-consuming and costly!
- Apart from setting up an Active Directory domain, the domain name needs to be registered with Domain Name authority so that it is accessible over the internet. This is the case with most of the real domains. However, for test domains, the name registration step has to be skipped and we had to find a viable alternative.
Testing Strategy
To overcome the above challenges, we decided the following testing strategy:
- List down the different topologies and the type of domain controllers
- Set up these on AWS EC2 instances
- Explore the AWS-managed Directory Service
- Use the Public IP of the Active Directory Server which is setup on an EC2 instance as an alternative to a registered domain name.
- Setup Active Directory, join an Ubuntu instance to the Active Directory Domain. Run the API from a domain-joined instance, using the domain name.
Testing Scenarios
1) Single Domain:
The simplest domain design is a single domain. In a single domain design, all information is replicated to all of the domain controllers. This domain is the forest root domain, and it contains all of the user and group accounts in the forest. This model also causes the most replication traffic while keeping the administrative overheads to a minimum.
2) Read-Only Domain Controller:
A Read-Only Domain Controller (RODC) is a domain controller with a read-only replica of the Active Directory Domain Services database.
It is used in remote branch offices where placing a full Domain Controller is not physically safe. It helps branch office users to login locally without connection to the remote AD server. However, any modification to the user properties will be done at the main domain controller and then replicated to the RODC.
The setup includes an AD site setup first, and then an RODC.
In RODC we can’t create users or groups because we don’t have permission to modify anything in RODC. We can create the entities in the main DC and then the replication fetches the data to the RODC.
3) Domain Tree:
Domain tree consists of one parent domain and two or more child domains. At the root level of the forest there is only one domain.
4) Forest with 2 domains joined in a trust relationship:
Trust relationship between domains is a logical link between the two domains. This link enables authenticated users of one domain to access resources in the other domain. The trust can be one-way or two-way between the ‘trusting’ domain and the ‘trusted’ domain. The trust relationship is transitive.
The domains may be in the same forest or different forests. The domain name of a domain must be resolvable from the other domain, if it is in a different forest.
The trust between two root level domains of different forests is called ‘forest trust’.
5) AWS Directory Service:
AWS directory service is a managed service for Microsoft Active Directory which makes it easy to integrate the Active Directory with other AWS services. The directory service helps in hosting an Active Directory on the cloud, or integrating with an on-premises Active Directory service.
6) Sub-Domains:
A subdomain is a domain which is part of a larger domain. The domain which is not a subdomain must be a root domain.
7) Secure LDAP:
By default, LDAP communications between client and server are not encrypted. This is problematic especially since credentials are sent in plain text over the network when LDAP simple bind is used. The credentials can easily be compromised this way. Secure LDAP or LDAPS resolves this vulnerability by encrypting the data over the wire using SSL/TLS.
To enable a secure LDAP on a domain controller the following step were taken:
- Install an Enterprise root CA on a domain controller.
- Add a digital certificate on each domain controller.
API Sample Input and Output
Group Information API
Input Sample:
End-Point: http://localhost:8085/v1/group/demogroup
{ "bind_address": "<EC2 IPv4 ADDRESS>", "FQDN": "subdomain.domain.com", "password": "<passwd>", "username": "username@domain.com", "issecure" : false }
Response:
{ "instanceType" : [ "4" ], "member": [ "CN=FirstName LastName,OU=demoteam,DC=domain,DC=com" ], "memberOf": [ "CN=localgroup,OU=demoteam,DC=domain,DC=com" ], "name": [ "demogroup" ], "objectCategory": [ "CN=Group,CN=Schema,CN=configuration,DC=domain,DC=com" ] }
User Information API
Input Sample:
End-Point: http://localhost:8085/v1/user/username
{ "bind_address": "<EC2 IPv4 ADDRESS>", "FQDN": "subdomain.domain.com", "password": "<password>", "username": "username@domain.com", "issecure" : false }
Response:
{ "mail" : [ "username@domain.com" ], "memberOf": [ "CN=demogroup,OU=demoteam,DC=domain,DC=com", "CN=localgroup,OU=demoteam,DC=domain,DC=com" ], "name": [ "FirstName LastName" ], "objectCategory": [ "CN=Person,CN=Schema,CN=configuration,DC=domain,DC=com" ] }
Conclusion
Active Directory is a robust technology that allows managing all users and resources of an organization.
AWS provides nice infrastructure and a well-documented procedure to set up all different Active Directory topologies.
The AWS Directory Service is a cherry-on-the-pie which provides for integrating on-premises Active Directory with the AWS services, or set up the Active Directory on the cloud itself.
The Python-Flask based API layer for Active Directory queries worked just fine, thanks to the design choices, the Active Directory as a technology, and the AWS !!
Written By:
Sameeksha Chepe & Pradip Kothawale