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:
parent
4240d55fe4
commit
449dcea821
@ -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,20 +21,47 @@ 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 not os.getenv("KRB5_KTNAME"):
|
if config.AUTHENTICATION_BACKEND == "kerberos":
|
||||||
|
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)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
principal = kerberos.getServerPrincipalDetails("HTTP", FQDN)
|
principal = kerberos.getServerPrincipalDetails("HTTP", FQDN)
|
||||||
except kerberos.KrbError as exc:
|
except kerberos.KrbError as exc:
|
||||||
click.echo("Failed to initialize Kerberos, reason: %s" % exc, err=True)
|
click.echo("Failed to initialize Kerberos, reason: %s" % exc, err=True)
|
||||||
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):
|
||||||
|
@ -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!")
|
||||||
|
@ -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])
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user