1
0
mirror of https://github.com/laurivosandi/certidude synced 2024-09-28 21:11:42 +00:00

Refactor LDAP authentication

* ldap uri can be specified in /etc/certidude/server.conf now
* /etc/ldap/ldap.conf is ignored
This commit is contained in:
Lauri Võsandi 2017-01-25 09:43:19 +00:00
parent 175f7f5d53
commit cca9d2ab2d
5 changed files with 44 additions and 46 deletions

View File

@ -44,7 +44,6 @@ class SessionResource(object):
@login_required @login_required
@event_source @event_source
def on_get(self, req, resp): def on_get(self, req, resp):
return dict( return dict(
user = dict( user = dict(
name=req.context.get("user").name, name=req.context.get("user").name,

View File

@ -122,9 +122,9 @@ def authenticate(optional=False):
import ldap import ldap
if not req.auth: if not req.auth:
resp.append_header("WWW-Authenticate", "Basic") raise falcon.HTTPUnauthorized("Unauthorized",
raise falcon.HTTPUnauthorized("Forbidden", "No authentication header provided",
"Please authenticate with %s domain account or supply UPN" % const.DOMAIN) ("Basic",))
if not req.auth.startswith("Basic "): if not req.auth.startswith("Basic "):
raise falcon.HTTPForbidden("Forbidden", "Bad header: %s" % req.auth) raise falcon.HTTPForbidden("Forbidden", "Bad header: %s" % req.auth)
@ -133,26 +133,35 @@ def authenticate(optional=False):
basic, token = req.auth.split(" ", 1) basic, token = req.auth.split(" ", 1)
user, passwd = b64decode(token).split(":", 1) user, passwd = b64decode(token).split(":", 1)
for server in config.LDAP_SERVERS: click.echo("Connecting to %s as %s" % (config.LDAP_AUTHENTICATION_URI, user))
click.echo("Connecting to %s as %s" % (server, user)) conn = ldap.initialize(config.LDAP_AUTHENTICATION_URI)
conn = ldap.initialize(server)
conn.set_option(ldap.OPT_REFERRALS, 0) conn.set_option(ldap.OPT_REFERRALS, 0)
if "@" not in user:
user = "%s@%s" % (user, const.DOMAIN)
logger.debug("Expanded username to %s", user)
try: try:
conn.simple_bind_s(user if "@" in user else "%s@%s" % (user, const.DOMAIN), passwd) conn.simple_bind_s(user, passwd)
except ldap.LDAPError, e: except ldap.STRONG_AUTH_REQUIRED:
resp.append_header("WWW-Authenticate", "Basic") logger.critical("LDAP server demands encryption, use ldaps:// instead of ldaps://")
raise
except ldap.SERVER_DOWN:
logger.critical("Failed to connect LDAP server at %s, are you sure LDAP server's CA certificate has been copied to this machine?",
config.LDAP_AUTHENTICATION_URI)
raise
except ldap.INVALID_CREDENTIALS:
logger.critical(u"LDAP bind authentication failed for user %s from %s", logger.critical(u"LDAP bind authentication failed for user %s from %s",
repr(user), req.context.get("remote_addr")) repr(user), req.context.get("remote_addr"))
raise falcon.HTTPUnauthorized("Forbidden", raise falcon.HTTPUnauthorized("Forbidden",
"Please authenticate with %s domain account or supply UPN" % const.DOMAIN) "Please authenticate with %s domain account or supply UPN" % const.DOMAIN,
("Basic",))
req.context["ldap_conn"] = conn req.context["ldap_conn"] = conn
break
else:
raise ValueError("No LDAP servers!")
req.context["user"] = User.objects.get(user) req.context["user"] = User.objects.get(user)
return func(resource, req, resp, *args, **kwargs) retval = func(resource, req, resp, *args, **kwargs)
conn.unbind_s()
return retval
def pam_authenticate(resource, req, resp, *args, **kwargs): def pam_authenticate(resource, req, resp, *args, **kwargs):

View File

@ -18,8 +18,10 @@ AUTHENTICATION_BACKENDS = set([j for j in
AUTHORIZATION_BACKEND = cp.get("authorization", "backend") # whitelist, ldap, posix AUTHORIZATION_BACKEND = cp.get("authorization", "backend") # whitelist, ldap, posix
ACCOUNTS_BACKEND = cp.get("accounts", "backend") # posix, ldap ACCOUNTS_BACKEND = cp.get("accounts", "backend") # posix, ldap
if ACCOUNTS_BACKEND == "ldap": LDAP_AUTHENTICATION_URI = cp.get("authentication", "ldap uri")
LDAP_GSSAPI_CRED_CACHE = cp.get("accounts", "ldap gssapi credential cache") LDAP_GSSAPI_CRED_CACHE = cp.get("accounts", "ldap gssapi credential cache")
LDAP_ACCOUNTS_URI = cp.get("accounts", "ldap uri")
LDAP_BASE = cp.get("accounts", "ldap base")
USER_SUBNETS = set([ipaddress.ip_network(j) for j in USER_SUBNETS = set([ipaddress.ip_network(j) for j in
cp.get("authorization", "user subnets").split(" ") if j]) cp.get("authorization", "user subnets").split(" ") if j])
@ -78,17 +80,4 @@ elif "ldap" == AUTHORIZATION_BACKEND:
else: else:
raise NotImplementedError("Unknown authorization backend '%s'" % AUTHORIZATION_BACKEND) raise NotImplementedError("Unknown authorization backend '%s'" % AUTHORIZATION_BACKEND)
for line in open("/etc/ldap/ldap.conf"):
line = line.strip().lower()
if "#" in line:
line, _ = line.split("#", 1)
if not " " in line:
continue
key, value = line.split(" ", 1)
if key == "uri":
LDAP_SERVERS = set([j for j in value.split(" ") if j])
click.echo("LDAP servers: %s" % " ".join(LDAP_SERVERS))
elif key == "base":
LDAP_BASE = value
# TODO: Check if we don't have base or servers # TODO: Check if we don't have base or servers

View File

@ -9,11 +9,12 @@ backends = pam
;backends = ldap ;backends = ldap
;backends = kerberos ldap ;backends = kerberos ldap
;backends = kerberos pam ;backends = kerberos pam
ldap uri = ldaps://dc1.example.com
[accounts] [accounts]
# The accounts backend specifies how the user's given name, surname and e-mail # The accounts backend specifies how the user's given name, surname and e-mail
# address are looked up. In case of 'posix' basically 'getent passwd' is performed, # address are looked up. In case of 'posix' basically 'getent passwd' is performed,
# in case of 'ldap' a search is performed on LDAP server specified in /etc/ldap/ldap.conf # in case of 'ldap' a search is performed on LDAP server specified by ldap uri
# with Kerberos credential cache initialized at path specified by environment variable KRB5CCNAME # with Kerberos credential cache initialized at path specified by environment variable KRB5CCNAME
# If certidude setup authority was performed correctly the credential cache should be # If certidude setup authority was performed correctly the credential cache should be
# updated automatically by /etc/cron.hourly/certidude # updated automatically by /etc/cron.hourly/certidude
@ -21,6 +22,8 @@ backends = pam
backend = posix backend = posix
;backend = ldap ;backend = ldap
ldap gssapi credential cache = /run/certidude/krb5cc ldap gssapi credential cache = /run/certidude/krb5cc
ldap uri = ldap://dc1.example.com
ldap base = {% if base %}{{ base }}{% else %}dc=example,dc=com{% endif %}
[authorization] [authorization]
# The authorization backend specifies how the users are authorized. # The authorization backend specifies how the users are authorized.

View File

@ -70,17 +70,15 @@ class DirectoryConnection(object):
raise ValueError("Ticket cache at %s not initialized, unable to " raise ValueError("Ticket cache at %s not initialized, unable to "
"authenticate with computer account against LDAP server!" % config.LDAP_GSSAPI_CRED_CACHE) "authenticate with computer account against LDAP server!" % config.LDAP_GSSAPI_CRED_CACHE)
os.environ["KRB5CCNAME"] = config.LDAP_GSSAPI_CRED_CACHE os.environ["KRB5CCNAME"] = config.LDAP_GSSAPI_CRED_CACHE
for server in config.LDAP_SERVERS: self.conn = ldap.initialize(config.LDAP_ACCOUNTS_URI)
self.conn = ldap.initialize(server)
self.conn.set_option(ldap.OPT_REFERRALS, 0) self.conn.set_option(ldap.OPT_REFERRALS, 0)
click.echo("Connecing to %s using Kerberos ticket cache from %s" % click.echo("Connecing to %s using Kerberos ticket cache from %s" %
(server, config.LDAP_GSSAPI_CRED_CACHE)) (config.LDAP_ACCOUNTS_URI, config.LDAP_GSSAPI_CRED_CACHE))
self.conn.sasl_interactive_bind_s('', ldap.sasl.gssapi()) self.conn.sasl_interactive_bind_s('', ldap.sasl.gssapi())
return self.conn return self.conn
raise ValueError("No LDAP servers specified!")
def __exit__(self, type, value, traceback): def __exit__(self, type, value, traceback):
self.conn.unbind_s self.conn.unbind_s()
class ActiveDirectoryUserManager(object): class ActiveDirectoryUserManager(object):