mirror of
				https://github.com/laurivosandi/certidude
				synced 2025-10-30 17:09:19 +00:00 
			
		
		
		
	Added diagrams and improved docs
This commit is contained in:
		
							
								
								
									
										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 | 
		Reference in New Issue
	
	Block a user