mirror of
				https://github.com/laurivosandi/certidude
				synced 2025-10-31 01:19:11 +00:00 
			
		
		
		
	Add preliminary PAM authentication backend
This commit is contained in:
		| @@ -7,6 +7,7 @@ import logging | ||||
| import os | ||||
| import re | ||||
| import socket | ||||
| from certidude import config | ||||
|  | ||||
| logger = logging.getLogger("api") | ||||
|  | ||||
| @@ -20,6 +21,7 @@ logger = logging.getLogger("api") | ||||
|  | ||||
| FQDN = socket.getaddrinfo(socket.gethostname(), 0, socket.AF_INET, 0, 0, socket.AI_CANONNAME)[0][3] | ||||
|  | ||||
| if config.AUTHENTICATION_BACKEND == "kerberos": | ||||
|     if not os.getenv("KRB5_KTNAME"): | ||||
|         click.echo("Kerberos keytab not specified, set environment variable 'KRB5_KTNAME'", err=True) | ||||
|         exit(250) | ||||
| @@ -31,9 +33,35 @@ except kerberos.KrbError as exc: | ||||
|         exit(249) | ||||
|     else: | ||||
|         click.echo("Kerberos enabled, service principal is HTTP/%s" % FQDN) | ||||
| else: | ||||
|     NotImplemented | ||||
|  | ||||
| def login_required(func): | ||||
|     def authenticate(resource, req, resp, *args, **kwargs): | ||||
|     def pam_authenticate(resource, req, resp, *args, **kwargs): | ||||
|         """ | ||||
|         Authenticate against PAM with WWW Basic Auth credentials | ||||
|         """ | ||||
|         authorization = req.get_header("Authorization") | ||||
|         if not authorization: | ||||
|             resp.append_header("WWW-Authenticate", "Basic") | ||||
|             raise falcon.HTTPUnauthorized("Forbidden", "Please authenticate") | ||||
|  | ||||
|         if not authorization.startswith("Basic "): | ||||
|             raise falcon.HTTPForbidden("Forbidden", "Bad header: %s" % authorization) | ||||
|  | ||||
|         from base64 import b64decode | ||||
|         basic, token = authorization.split(" ", 1) | ||||
|         user, passwd = b64decode(token).split(":", 1) | ||||
|  | ||||
|         import simplepam | ||||
|         if not simplepam.authenticate(user, passwd, "sshd"): | ||||
|             raise falcon.HTTPForbidden("Forbidden", "Invalid password") | ||||
|  | ||||
|         req.context["user"] = user | ||||
|         return func(resource, req, resp, *args, **kwargs) | ||||
|  | ||||
|  | ||||
|     def kerberos_authenticate(resource, req, resp, *args, **kwargs): | ||||
|         authorization = req.get_header("Authorization") | ||||
|  | ||||
|         if not authorization: | ||||
| @@ -82,7 +110,12 @@ def login_required(func): | ||||
|             # TODO: logger.error | ||||
|             raise falcon.HTTPForbidden("Forbidden", "Tried GSSAPI") | ||||
|  | ||||
|     return authenticate | ||||
|     if config.AUTHENTICATION_BACKEND == "kerberos": | ||||
|         return kerberos_authenticate | ||||
|     elif config.AUTHENTICATION_BACKEND == "pam": | ||||
|         return pam_authenticate | ||||
|     else: | ||||
|         NotImplemented | ||||
|  | ||||
|  | ||||
| def authorize_admin(func): | ||||
|   | ||||
| @@ -1006,6 +1006,7 @@ def certidude_serve(user, port, listen, enable_signature): | ||||
|     from wsgiref.simple_server import make_server, WSGIServer | ||||
|     from socketserver import ThreadingMixIn | ||||
|     from certidude.api import certidude_app, StaticResource | ||||
|     from certidude import config | ||||
|  | ||||
|     class ThreadingWSGIServer(ThreadingMixIn, WSGIServer): | ||||
|         pass | ||||
| @@ -1024,13 +1025,26 @@ def certidude_serve(user, port, listen, enable_signature): | ||||
|         from jinja2.debug import make_traceback as _make_traceback | ||||
|         "".encode("charmap") | ||||
|  | ||||
|         if config.AUTHENTICATION_BACKEND == "pam": | ||||
|             # PAM needs access to /etc/shadow | ||||
|             import grp | ||||
|             name, passwd, gid, mem = grp.getgrnam("shadow") | ||||
|             click.echo("Adding current user to shadow group due to PAM authentication backend") | ||||
|             os.setgroups([gid]) | ||||
|         else: | ||||
|             os.setgroups([]) | ||||
|  | ||||
|         _, _, uid, gid, gecos, root, shell = pwd.getpwnam(user) | ||||
|         if uid == 0: | ||||
|             click.echo("Please specify unprivileged user") | ||||
|             exit(254) | ||||
|         click.echo("Switching to user %s (uid=%d, gid=%d)" % (user, uid, gid)) | ||||
|  | ||||
|         os.setgid(gid) | ||||
|         os.setuid(uid) | ||||
|  | ||||
|         click.echo("Switched to user %s (uid=%d, gid=%d); member of groups %s" % | ||||
|             (user, uid, gid, ", ".join([str(j) for j in os.getgroups()]))) | ||||
|  | ||||
|         os.umask(0o007) | ||||
|     elif os.getuid() == 0: | ||||
|         click.echo("Warning: running as root, this is not recommended!") | ||||
|   | ||||
| @@ -14,6 +14,9 @@ FQDN = socket.getaddrinfo(socket.gethostname(), 0, socket.AF_INET, 0, 0, socket. | ||||
| cp = configparser.ConfigParser() | ||||
| cp.readfp(codecs.open("/etc/certidude/server.conf", "r", "utf8")) | ||||
|  | ||||
| AUTHENTICATION_BACKEND = cp.get("authentication", "backend") # kerberos, pam | ||||
| AUTHORIZATION_BACKEND = cp.get("authorization", "backend") # whitelist, ldap, pam | ||||
|  | ||||
| ADMIN_USERS = set([j for j in  cp.get("authorization", "admin_users").split(" ") if j]) | ||||
| ADMIN_SUBNETS = set([ipaddress.ip_network(j) for j in cp.get("authorization", "admin_subnets").split(" ") if j]) | ||||
| AUTOSIGN_SUBNETS = set([ipaddress.ip_network(j) for j in cp.get("authorization", "autosign_subnets").split(" ") if j]) | ||||
|   | ||||
| @@ -18,4 +18,5 @@ pyOpenSSL==0.15.1 | ||||
| python-mimeparse==0.1.4 | ||||
| requests==2.2.1 | ||||
| setproctitle==1.1.9 | ||||
| simplepam==0.1.5 | ||||
| six==1.9.0 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user