auth-ldap: added connection pinning and timeout option to ldap plugin

This commit is contained in:
Marcin Kuzminski 2018-03-27 11:51:37 +02:00
parent 1f8b60a56a
commit a8428e7271
2 changed files with 56 additions and 11 deletions

View file

@ -86,6 +86,7 @@ class TestAuthSettingsView(object):
'host': 'dc.example.com',
'port': '999',
'timeout': 3600,
'tls_kind': 'PLAIN',
'tls_reqcert': 'NEVER',

View file

@ -22,6 +22,7 @@
RhodeCode authentication plugin for LDAP
"""
import socket
import re
import colander
import logging
@ -90,6 +91,16 @@ class LdapSettingsSchema(AuthnPluginSettingsSchemaBase):
title=_('Port'),
validator=colander.Range(min=0, max=65536),
widget='int')
timeout = colander.SchemaNode(
colander.Int(),
default=60 * 5,
description=_('Timeout for LDAP connection'),
preparer=strip_whitespace,
title=_('Connection timeout'),
validator=colander.Range(min=1),
widget='int')
dn_user = colander.SchemaNode(
colander.String(),
default='',
@ -191,19 +202,47 @@ class LdapSettingsSchema(AuthnPluginSettingsSchemaBase):
class AuthLdap(object):
def _build_servers(self):
def host_resolver(host, port):
"""
Main work for this function is to prevent ldap connection issues,
and detect them early using a "greenified" sockets
"""
host = host.strip()
log.info('Resolving LDAP host %s', host)
try:
ip = socket.gethostbyname(host)
log.info('Got LDAP server %s ip %s', host, ip)
except Exception:
raise LdapConnectionError(
'Failed to resolve host: `{}`'.format(host))
log.info('Checking LDAP IP access %s', ip)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, int(port)))
s.shutdown(socket.SHUT_RD)
except Exception:
raise LdapConnectionError(
'Failed to connect to host: `{}:{}`'.format(host, port))
return '{}:{}'.format(host, port)
port = self.LDAP_SERVER_PORT
return ', '.join(
["{}://{}:{}".format(
self.ldap_server_type, host.strip(), self.LDAP_SERVER_PORT)
["{}://{}".format(
self.ldap_server_type, host_resolver(host, port))
for host in self.SERVER_ADDRESSES])
def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3,
search_scope='SUBTREE', attr_login='uid',
ldap_filter=''):
ldap_filter='', timeout=None):
if ldap == Missing:
raise LdapImportError("Missing or incompatible ldap library")
self.debug = False
self.timeout = timeout or 60 * 5
self.ldap_version = ldap_version
self.ldap_server_type = 'ldap'
@ -231,22 +270,25 @@ class AuthLdap(object):
self.LDAP_FILTER = safe_str(ldap_filter)
def _get_ldap_conn(self):
log.debug('initializing LDAP connection to:%s', self.LDAP_SERVER)
if self.debug:
ldap.set_option(ldap.OPT_DEBUG_LEVEL, 255)
if hasattr(ldap, 'OPT_X_TLS_CACERTDIR'):
ldap.set_option(ldap.OPT_X_TLS_CACERTDIR,
'/etc/openldap/cacerts')
ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON)
ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 60 * 10)
ldap.set_option(ldap.OPT_TIMEOUT, 60 * 10)
ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, '/etc/openldap/cacerts')
if self.TLS_KIND != 'PLAIN':
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT)
log.debug('initializing LDAP connection to:%s', self.LDAP_SERVER)
ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON)
# init connection now
ldap_conn = ldap.initialize(self.LDAP_SERVER)
ldap_conn.set_option(ldap.OPT_NETWORK_TIMEOUT, self.timeout)
ldap_conn.set_option(ldap.OPT_TIMEOUT, self.timeout)
ldap_conn.timeout = self.timeout
if self.ldap_version == 2:
ldap_conn.protocol = ldap.VERSION2
else:
@ -446,6 +488,7 @@ class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
'attr_login': settings.get('attr_login'),
'ldap_version': 3,
'ldap_filter': settings.get('filter'),
'timeout': settings.get('timeout')
}
ldap_attrs = self.try_dynamic_binding(username, password, ldap_args)
@ -496,3 +539,4 @@ class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
except (Exception,):
log.exception("Other exception")
return None