1
0
mirror of https://github.com/laurivosandi/certidude synced 2024-12-23 00:25:18 +00:00

Add preliminary PAM authentication backend

This commit is contained in:
Lauri Võsandi 2016-02-29 23:06:42 +02:00
parent 4240d55fe4
commit 449dcea821
4 changed files with 63 additions and 12 deletions

View File

@ -7,6 +7,7 @@ import logging
import os import os
import re import re
import socket import socket
from certidude import config
logger = logging.getLogger("api") 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] 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"): if not os.getenv("KRB5_KTNAME"):
click.echo("Kerberos keytab not specified, set environment variable 'KRB5_KTNAME'", err=True) click.echo("Kerberos keytab not specified, set environment variable 'KRB5_KTNAME'", err=True)
exit(250) exit(250)
@ -31,9 +33,35 @@ except kerberos.KrbError as exc:
exit(249) exit(249)
else: else:
click.echo("Kerberos enabled, service principal is HTTP/%s" % FQDN) click.echo("Kerberos enabled, service principal is HTTP/%s" % FQDN)
else:
NotImplemented
def login_required(func): 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") authorization = req.get_header("Authorization")
if not authorization: if not authorization:
@ -82,7 +110,12 @@ def login_required(func):
# TODO: logger.error # TODO: logger.error
raise falcon.HTTPForbidden("Forbidden", "Tried GSSAPI") 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): def authorize_admin(func):

View File

@ -1006,6 +1006,7 @@ def certidude_serve(user, port, listen, enable_signature):
from wsgiref.simple_server import make_server, WSGIServer from wsgiref.simple_server import make_server, WSGIServer
from socketserver import ThreadingMixIn from socketserver import ThreadingMixIn
from certidude.api import certidude_app, StaticResource from certidude.api import certidude_app, StaticResource
from certidude import config
class ThreadingWSGIServer(ThreadingMixIn, WSGIServer): class ThreadingWSGIServer(ThreadingMixIn, WSGIServer):
pass pass
@ -1024,13 +1025,26 @@ def certidude_serve(user, port, listen, enable_signature):
from jinja2.debug import make_traceback as _make_traceback from jinja2.debug import make_traceback as _make_traceback
"".encode("charmap") "".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) _, _, uid, gid, gecos, root, shell = pwd.getpwnam(user)
if uid == 0: if uid == 0:
click.echo("Please specify unprivileged user") click.echo("Please specify unprivileged user")
exit(254) exit(254)
click.echo("Switching to user %s (uid=%d, gid=%d)" % (user, uid, gid))
os.setgid(gid) os.setgid(gid)
os.setuid(uid) 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) os.umask(0o007)
elif os.getuid() == 0: elif os.getuid() == 0:
click.echo("Warning: running as root, this is not recommended!") click.echo("Warning: running as root, this is not recommended!")

View File

@ -14,6 +14,9 @@ FQDN = socket.getaddrinfo(socket.gethostname(), 0, socket.AF_INET, 0, 0, socket.
cp = configparser.ConfigParser() cp = configparser.ConfigParser()
cp.readfp(codecs.open("/etc/certidude/server.conf", "r", "utf8")) 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_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]) 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]) AUTOSIGN_SUBNETS = set([ipaddress.ip_network(j) for j in cp.get("authorization", "autosign_subnets").split(" ") if j])

View File

@ -18,4 +18,5 @@ pyOpenSSL==0.15.1
python-mimeparse==0.1.4 python-mimeparse==0.1.4
requests==2.2.1 requests==2.2.1
setproctitle==1.1.9 setproctitle==1.1.9
simplepam==0.1.5
six==1.9.0 six==1.9.0