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

Added diagrams and improved docs

This commit is contained in:
Lauri Võsandi 2015-08-16 18:09:06 +03:00
parent e2f27078d1
commit f92853bedb
4 changed files with 35 additions and 14 deletions

View File

@ -5,9 +5,10 @@ Introduction
------------ ------------
Certidude is a novel X.509 Certificate Authority management tool Certidude is a novel X.509 Certificate Authority management tool
with privilege isolation mechanism aiming to with privilege isolation mechanism and Kerberos authentication aiming to
eventually support PKCS#11 and in far future WebCrypto. eventually support PKCS#11 and in far future WebCrypto.
.. figure:: doc/usecase-diagram.png
Features Features
-------- --------
@ -106,19 +107,19 @@ Use web interface or following to sign a certificate on Certidude server:
Production deployment Production deployment
--------------------- ---------------------
Install uWSGI: Install ``nginx`` and ``uwsgi``:
.. code:: bash .. code:: bash
apt-get install nginx uwsgi uwsgi-plugin-python3 apt-get install nginx uwsgi uwsgi-plugin-python3
To set up ``nginx`` and ``uwsgi`` is suggested: For easy setup following is reccommended:
.. code:: bash .. code:: bash
certidude setup production certidude setup production
Otherwise manually configure uUWSGI application in ``/etc/uwsgi/apps-available/certidude.ini``: Otherwise manually configure ``uwsgi`` application in ``/etc/uwsgi/apps-available/certidude.ini``:
.. code:: ini .. code:: ini
@ -136,8 +137,12 @@ Otherwise manually configure uUWSGI application in ``/etc/uwsgi/apps-available/c
callable = app callable = app
chmod-socket = 660 chmod-socket = 660
chown-socket = certidude:www-data chown-socket = certidude:www-data
buffer-size = 32768
env = PUSH_PUBLISH=http://localhost/event/publish/%(channel)s env = PUSH_PUBLISH=http://localhost/event/publish/%(channel)s
env = PUSH_SUBSCRIBE=http://localhost/event/subscribe/%(channel)s env = PUSH_SUBSCRIBE=http://localhost/event/subscribe/%(channel)s
env = LANG=C.UTF-8
env = LC_ALL=C.UTF-8
env = KRB5_KTNAME=/etc/certidude.keytab
Also enable the application: Also enable the application:
@ -272,11 +277,11 @@ to generate user whitelist via LDAP:
.. code:: bash .. code:: bash
ldapsearch -H ldap://dc1.id.stipit.com -s sub -x -LLL \ ldapsearch -H ldap://dc1.example.com -s sub -x -LLL \
-D 'cn=certidude,cn=Users,dc=id,dc=stipit,dc=com' \ -D 'cn=certidude,cn=Users,dc=example,dc=com' \
-w 'certidudepass' \ -w 'certidudepass' \
-b 'ou=sso,dc=id,dc=stipit,dc=com' \ -b 'dc=example,dc=com' \
'(objectClass=user)' sAMAccountName userPrincipalName givenName sn \ '(&(objectClass=user)(memberOf=cn=Domain Admins,cn=Users,dc=example,dc=com))' sAMAccountName userPrincipalName givenName sn \
| python3 -c "import ldif3; import sys; [sys.stdout.write('%s:%s:%s:%s\n' % (a.pop('sAMAccountName')[0], a.pop('userPrincipalName')[0], a.pop('givenName')[0], a.pop('sn')[0])) for _, a in ldif3.LDIFParser(sys.stdin.buffer).parse()]" \ | python3 -c "import ldif3; import sys; [sys.stdout.write('%s:%s:%s:%s\n' % (a.pop('sAMAccountName')[0], a.pop('userPrincipalName')[0], a.pop('givenName')[0], a.pop('sn')[0])) for _, a in ldif3.LDIFParser(sys.stdin.buffer).parse()]" \
> /run/certidude/user.whitelist > /run/certidude/user.whitelist

View File

@ -1,5 +1,6 @@
import re import re
import falcon import falcon
import ipaddress
import os import os
import json import json
import types import types
@ -23,9 +24,25 @@ def omit(**kwargs):
def authorize_admin(func): def authorize_admin(func):
def wrapped(self, req, resp, *args, **kwargs): def wrapped(self, req, resp, *args, **kwargs):
authority = kwargs.get("ca")
# Parse remote IPv4/IPv6 address
remote_addr = ipaddress.ip_network(req.env["REMOTE_ADDR"])
# Check for administration subnet whitelist
print("Comparing:", authority.admin_subnets, "To:", remote_addr)
for subnet in authority.admin_subnets:
if subnet.overlaps(remote_addr):
break
else:
raise falcon.HTTPForbidden("Forbidden", "Remote address %s not whitelisted" % remote_addr)
# Check for username whitelist
kerberos_username, kerberos_realm = kwargs.get("user") kerberos_username, kerberos_realm = kwargs.get("user")
if kerberos_username not in kwargs.get("ca").admin_users: if kerberos_username not in authority.admin_users:
raise falcon.HTTPForbidden("User %s not whitelisted" % kerberos_username) raise falcon.HTTPForbidden("Forbidden", "User %s not whitelisted" % kerberos_username)
# Retain username, TODO: Better abstraction with username, e-mail, sn, gn?
kwargs["user"] = kerberos_username kwargs["user"] = kerberos_username
return func(self, req, resp, *args, **kwargs) return func(self, req, resp, *args, **kwargs)
return wrapped return wrapped
@ -34,7 +51,6 @@ def authorize_admin(func):
def pop_certificate_authority(func): def pop_certificate_authority(func):
def wrapped(self, req, resp, *args, **kwargs): def wrapped(self, req, resp, *args, **kwargs):
kwargs["ca"] = self.config.instantiate_authority(kwargs["ca"]) kwargs["ca"] = self.config.instantiate_authority(kwargs["ca"])
print(func)
return func(self, req, resp, *args, **kwargs) return func(self, req, resp, *args, **kwargs)
return wrapped return wrapped
@ -86,7 +102,6 @@ def templatize(path):
def wrapper(func): def wrapper(func):
def wrapped(instance, req, resp, *args, **kwargs): def wrapped(instance, req, resp, *args, **kwargs):
assert not req.get_param("unicode") or req.get_param("unicode") == u"", "Unicode sanity check failed" assert not req.get_param("unicode") or req.get_param("unicode") == u"", "Unicode sanity check failed"
print("templatize would call", func, "with", args, kwargs)
r = func(instance, req, resp, *args, **kwargs) r = func(instance, req, resp, *args, **kwargs)
r.pop("self") r.pop("self")
if not resp.body: if not resp.body:
@ -212,7 +227,7 @@ class RequestListResource(CertificateAuthorityBase):
Submit certificate signing request (CSR) in PEM format Submit certificate signing request (CSR) in PEM format
""" """
# Parse remote IPv4/IPv6 address # Parse remote IPv4/IPv6 address
remote_addr = ipaddress.ip_address(req.env["REMOTE_ADDR"]) remote_addr = ipaddress.ip_network(req.env["REMOTE_ADDR"])
# Check for CSR submission whitelist # Check for CSR submission whitelist
if ca.request_subnets: if ca.request_subnets:
@ -220,7 +235,7 @@ class RequestListResource(CertificateAuthorityBase):
if subnet.overlaps(remote_addr): if subnet.overlaps(remote_addr):
break break
else: else:
raise falcon.HTTPForbidden("IP address %s not whitelisted" % remote_addr) raise falcon.HTTPForbidden("Forbidden", "IP address %s not whitelisted" % remote_addr)
if req.get_header("Content-Type") != "application/pkcs10": if req.get_header("Content-Type") != "application/pkcs10":
raise falcon.HTTPUnsupportedMediaType( raise falcon.HTTPUnsupportedMediaType(
@ -308,6 +323,7 @@ class CertificateAuthorityResource(CertificateAuthorityBase):
class IndexResource(CertificateAuthorityBase): class IndexResource(CertificateAuthorityBase):
@login_required @login_required
@pop_certificate_authority @pop_certificate_authority
@authorize_admin
@templatize("index.html") @templatize("index.html")
def on_get(self, req, resp, ca, user): def on_get(self, req, resp, ca, user):
return locals() return locals()

BIN
doc/usecase-diagram.dia Normal file

Binary file not shown.

BIN
doc/usecase-diagram.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB