mirror of
				https://github.com/laurivosandi/certidude
				synced 2025-10-31 09:29:13 +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:
		| @@ -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, | ||||||
|   | |||||||
| @@ -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) |  | ||||||
|                 try: |  | ||||||
|                     conn.simple_bind_s(user if "@" in user else "%s@%s" % (user, const.DOMAIN), passwd) |  | ||||||
|                 except ldap.LDAPError, e: |  | ||||||
|                     resp.append_header("WWW-Authenticate", "Basic") |  | ||||||
|                     logger.critical(u"LDAP bind authentication failed for user %s from  %s", |  | ||||||
|                         repr(user), req.context.get("remote_addr")) |  | ||||||
|                     raise falcon.HTTPUnauthorized("Forbidden", |  | ||||||
|                         "Please authenticate with %s domain account or supply UPN" % const.DOMAIN) |  | ||||||
|  |  | ||||||
|                 req.context["ldap_conn"] = conn |             if "@" not in user: | ||||||
|                 break |                 user = "%s@%s" % (user, const.DOMAIN) | ||||||
|             else: |                 logger.debug("Expanded username to %s", user) | ||||||
|                 raise ValueError("No LDAP servers!") |  | ||||||
|  |  | ||||||
|  |             try: | ||||||
|  |                 conn.simple_bind_s(user, passwd) | ||||||
|  |             except ldap.STRONG_AUTH_REQUIRED: | ||||||
|  |                 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", | ||||||
|  |                     repr(user), req.context.get("remote_addr")) | ||||||
|  |                 raise falcon.HTTPUnauthorized("Forbidden", | ||||||
|  |                     "Please authenticate with %s domain account or supply UPN" % const.DOMAIN, | ||||||
|  | 		("Basic",)) | ||||||
|  |  | ||||||
|  |             req.context["ldap_conn"] = conn | ||||||
|             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): | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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. | ||||||
|   | |||||||
| @@ -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" % |             (config.LDAP_ACCOUNTS_URI, config.LDAP_GSSAPI_CRED_CACHE)) | ||||||
|                 (server, 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): | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user