summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Stack <bgstack15@gmail.com>2021-06-24 11:35:00 -0400
committerB. Stack <bgstack15@gmail.com>2021-06-24 11:35:00 -0400
commit23837ea33e62d279a039931f9cee781112b2f3ea (patch)
treecb06b3a91f61a008e746a33422e33012ad78de81
parentadd www-negotiate basic header to /login/basic (diff)
downloadsession_app-23837ea33e62d279a039931f9cee781112b2f3ea.tar.gz
session_app-23837ea33e62d279a039931f9cee781112b2f3ea.tar.bz2
session_app-23837ea33e62d279a039931f9cee781112b2f3ea.zip
add dns-based ldap domain controller lookup
and rotate through the returned list of servers, per request!
-rwxr-xr-xsession_app.py.publish25
-rw-r--r--session_ldap.py20
2 files changed, 42 insertions, 3 deletions
diff --git a/session_app.py.publish b/session_app.py.publish
index adfd0ad..fd403c2 100755
--- a/session_app.py.publish
+++ b/session_app.py.publish
@@ -10,11 +10,11 @@
# future: https://code.tutsplus.com/tutorials/flask-authentication-with-ldap--cms-23101
# better timeout session: https://stackoverflow.com/a/49891626/3569534
# store "formdata" in session for changing the basic auth to form data for the ldap login https://stackoverflow.com/a/56904875/3569534
+# modify url from urlparse https://stackoverflow.com/a/21629125/3569534
# Improve:
# move all configs to config file
# move all references to references section
# accept a bind credential so we can perform lookups of users who match "uid=%s" under a basedn.
-# accept a ldap dns domain name, and a SRV lookup for _tcp._ldap
# Run:
# FLASK_APP=session_app.py FLASK_DEBUG=1 flask run --host 0.0.0.0
# Dependencies:
@@ -28,6 +28,7 @@ from functools import wraps
import binascii, datetime
import os
import session_ldap
+from urllib.parse import urlparse
DEBUG=True
app = Flask(__name__)
@@ -36,7 +37,7 @@ app.debug=True
secret_key_value = os.urandom(24)
secret_key_value_hex_encoded = binascii.hexlify(secret_key_value)
app.config['SECRET_KEY'] = secret_key_value_hex_encoded
-app.config['LDAP_URI'] = "ldaps://dns1.ipa.internal.com:636"
+app.config['LDAP_URI'] = "ldaps://ipa.internal.com:636"
app.config['LDAP_USER_BASEDN'] = "cn=users,cn=accounts,dc=ipa,dc=internal,dc=com"
app.config['LDAP_GROUP_BASEDN'] = "cn=groups,cn=accounts,dc=ipa,dc=internal,dc=com"
app.config['LDAP_USER_FORMAT'] = "uid=%s,cn=users,cn=accounts,dc=ipa,dc=internal,dc=com"
@@ -183,8 +184,26 @@ def login(user="None"):
def ldap_login(username,password):
#print(f"DEBUG: Trying user {username} with pw '{password}'")
+ # on first ldap_login attempt, cache this lookup result:
+ if 'LDAP_HOSTS' not in app.config:
+ this_domain = urlparse(app.config['LDAP_URI']).hostname
+ app.config['LDAP_HOSTS'] = session_ldap.list_ldap_servers_for_domain(this_domain)
+ else:
+ # rotate them! So every ldap_login attempt will use the next ldap server in the list.
+ this_list = app.config['LDAP_HOSTS']
+ a = this_list[0]
+ this_list.append(a)
+ this_list.pop(0)
+ app.config['LDAP_HOSTS'] = this_list
+ # construct a new, full uri.
+ this_netloc = app.config['LDAP_HOSTS'][0]
+ up = urlparse(app.config['LDAP_URI'])
+ if up.port:
+ this_netloc += f":{up.port}"
+ this_uri = up._replace(netloc=this_netloc).geturl()
+ # Perform the ldap interactions
user = session_ldap.authenticated_user(
- app.config['LDAP_URI'],
+ this_uri,
app.config['LDAP_USER_FORMAT'],
username,
password
diff --git a/session_ldap.py b/session_ldap.py
index b478ef5..d12f008 100644
--- a/session_ldap.py
+++ b/session_ldap.py
@@ -28,3 +28,23 @@ def authenticated_user(server_uri, user_format, username, password):
# print("Either an ldap password is required, or we had another bind error.")
# return False
return False
+
+def list_ldap_servers_for_domain(domain):
+ # return list of hostnames from the _ldap._tcp.{domain} SRV lookup
+ try:
+ import dns
+ import dns.resolver
+ except:
+ print("Need python3-dns installed for dns lookups.")
+ return [domain]
+ namelist = []
+ try:
+ query = dns.resolver.query(f"_ldap._tcp.{domain}","SRV")
+ except dns.resolver.NXDOMAIN:
+ # no records exist that match the request, so we were probably given a specific hostname, and an empty query will trigger the logic below that will add the original domain to the list.
+ query = []
+ for i in query:
+ namelist.append(i.target.to_text().rstrip("."))
+ if not len(namelist):
+ namelist.append(domain)
+ return namelist
bgstack15