mirror of
https://github.com/laurivosandi/certidude
synced 2024-12-22 16:25:17 +00:00
Added diagrams and improved docs
This commit is contained in:
parent
e2f27078d1
commit
f92853bedb
21
README.rst
21
README.rst
@ -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
|
||||||
|
|
||||||
|
@ -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
BIN
doc/usecase-diagram.dia
Normal file
Binary file not shown.
BIN
doc/usecase-diagram.png
Normal file
BIN
doc/usecase-diagram.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
Loading…
Reference in New Issue
Block a user