diff --git a/nginx-ldap-auth-daemon b/nginx-ldap-auth-daemon index b4cdeaa..48f9f01 100755 --- a/nginx-ldap-auth-daemon +++ b/nginx-ldap-auth-daemon @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import os import pwd import grp @@ -7,14 +7,23 @@ import signal import base64 import ldap import argparse -from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler +from http.server import HTTPServer, BaseHTTPRequestHandler +import logging + +logger = logging.getLogger() +logger.addHandler(logging.StreamHandler()) +logger.setLevel(logging.INFO) # ----------------------------------------------------------------------------- # Requests are processed in separate thread import threading -from SocketServer import ThreadingMixIn +from socketserver import ThreadingMixIn + + class AuthHTTPServer(ThreadingMixIn, HTTPServer): pass + + # ----------------------------------------------------------------------------- # Requests are processed in separate process # from SocketServer import ForkingMixIn @@ -41,13 +50,13 @@ def read_conf(fname): data = line.strip().split() if len(data) > 1 and data[0] in opts: conf[data[0]] = ' '.join(data[1:]) - except: - print "Unable to read {} as uid {}: {}".format(fname, os.getuid(), sys.exc_info()) + except Exception: + logger.critical("Unable to read {} as uid {}: {}".format(fname, os.getuid(), sys.exc_info())) sys.exit(1) for o in opts[:4]: if o not in conf: - print "Mandatory parameter '{}' was not found in config file {}!".format(o, fname) + logger.critical("Mandatory parameter '{}' was not found in config file {}!".format(o, fname)) sys.exit(1) @@ -70,19 +79,24 @@ def check_auth(user, passwd, allowusr, allowgr): proto = 'ldap://' if conf['ssl'] != 'on' else 'ldaps://' for host in conf['host'].split(): try: - ldap_connection = ldap.initialize(proto + host) + ldap_connection = ldap.initialize(proto + host, bytes_mode=False) ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) ldap_connection.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_ALLOW) ldap_connection.set_option(ldap.OPT_REFERRALS, 0) # MS AD ldap_connection.set_option(ldap.OPT_NETWORK_TIMEOUT, 3) ldap_connection.simple_bind_s(conf['binddn'], conf['bindpw']) - data = ldap_connection.search_s(base=conf['base'], scope=ldap.SCOPE_SUBTREE, filterstr='(&(objectClass=user)(sAMAccountName=' + user + '))') + data = ldap_connection.search_s(base=conf['base'], scope=ldap.SCOPE_SUBTREE, + filterstr='(&(objectClass=user)(sAMAccountName=' + user + '))') if data: data = data[0][1] + # check if search found user + if 'distinguishedName' not in data: + return False # check password try: - ldap_connection.simple_bind_s(data['distinguishedName'][0], passwd) + ldap_connection.simple_bind_s(data['distinguishedName'][0].decode('utf-8'), passwd) except ldap.INVALID_CREDENTIALS: + logger.warning("Login failed for user {}".format(user)) return False # check allowed users if allowusr and user.lower() in [x.lower().strip() for x in allowusr.split(',')]: @@ -94,12 +108,14 @@ def check_auth(user, passwd, allowusr, allowgr): groups += data['msSFU30PosixMemberOf'] for g in [x.lower().strip() for x in allowgr.split(',')]: for group in groups: - if group.lower().startswith('cn={},'.format(g)): + if group.decode('utf-8').lower().startswith('cn={},'.format(g)): return True # user found but not in allowed return False if allowusr or allowgr else True except (ldap.CONNECT_ERROR, ldap.SERVER_DOWN): - pass # try next server + logger.warning('Connection error to server {}'.format(host)) + # try next server + continue finally: if ldap_connection: ldap_connection.unbind() @@ -109,19 +125,21 @@ def check_auth(user, passwd, allowusr, allowgr): class LDAPAuthHandler(BaseHTTPRequestHandler): def do_GET(self): try: - auth_header = self.headers.getheader('Authorization') + auth_header = self.headers.get('Authorization') if auth_header and auth_header.lower().startswith('basic '): - user, passwd = base64.b64decode(auth_header[6:]).split(':', 1) - if check_auth(user, passwd, self.headers.getheader('X-Ldap-Allowed-Usr'), self.headers.getheader('X-Ldap-Allowed-Grp')): + user, passwd = base64.b64decode(auth_header[6:]).decode('utf-8').split(':', 1) + if check_auth(user, passwd, self.headers.get('X-Ldap-Allowed-Usr'), + self.headers.get('X-Ldap-Allowed-Grp')): self.send_response(200) return self.send_response(401) - realm = self.headers.getheader('X-Ldap-Realm') + realm = self.headers.get('X-Ldap-Realm') if not realm: realm = 'Authorization required' self.send_header('WWW-Authenticate', 'Basic realm="{}"'.format(realm)) self.send_header('Cache-Control', 'no-cache') - except: + except Exception: + logger.exception('Unhandled exception in LdapAuthHandler') self.send_response(500) self.send_header('X-Error-Message', sys.exc_info()[1]) finally: @@ -132,7 +150,8 @@ if __name__ == '__main__': parser = argparse.ArgumentParser(description="""Simple Nginx LDAP authentication helper.""") parser.add_argument('--host', default="localhost", help="host to bind (Default: localhost)") parser.add_argument('-p', '--port', type=int, default=8888, help="port to bind (Default: 8888)") - parser.add_argument('-c', '--config', default='/etc/pam_ldap.conf', help="config with LDAP creds (Default: /etc/pam_ldap.conf)") + parser.add_argument('-c', '--config', default='/etc/pam_ldap.conf', + help="config with LDAP creds (Default: /etc/pam_ldap.conf)") args = parser.parse_args() read_conf(args.config)