auth-ldap: added connection pinning and timeout option to ldap plugin
This commit is contained in:
parent
1f8b60a56a
commit
a8428e7271
2 changed files with 56 additions and 11 deletions
|
|
@ -86,6 +86,7 @@ class TestAuthSettingsView(object):
|
|||
|
||||
'host': 'dc.example.com',
|
||||
'port': '999',
|
||||
'timeout': 3600,
|
||||
'tls_kind': 'PLAIN',
|
||||
'tls_reqcert': 'NEVER',
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue