mirror of
				https://github.com/laurivosandi/certidude
				synced 2025-10-31 09:29:13 +00:00 
			
		
		
		
	Merge branch 'master' of github.com:laurivosandi/certidude
This commit is contained in:
		| @@ -154,8 +154,6 @@ Otherwise manually configure ``uwsgi`` application in ``/etc/uwsgi/apps-availabl | |||||||
|     chmod-socket = 660 |     chmod-socket = 660 | ||||||
|     chown-socket = certidude:www-data |     chown-socket = certidude:www-data | ||||||
|     buffer-size = 32768 |     buffer-size = 32768 | ||||||
|     env = PUSH_PUBLISH=http://localhost/event/publish/%(channel)s |  | ||||||
|     env = PUSH_SUBSCRIBE=http://localhost/event/subscribe/%(channel)s |  | ||||||
|     env = LANG=C.UTF-8 |     env = LANG=C.UTF-8 | ||||||
|     env = LC_ALL=C.UTF-8 |     env = LC_ALL=C.UTF-8 | ||||||
|     env = KRB5_KTNAME=/etc/certidude.keytab |     env = KRB5_KTNAME=/etc/certidude.keytab | ||||||
| @@ -232,6 +230,11 @@ Also adjust ``/etc/nginx/nginx.conf``: | |||||||
|         include /etc/nginx/sites-enabled/*; |         include /etc/nginx/sites-enabled/*; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | In your CA ssl.cnf make sure Certidude is aware of your nginx setup: | ||||||
|  |  | ||||||
|  |     publish_certificate_url = http://push.example.com/event/publish/%(request_sha1sum)s | ||||||
|  |     subscribe_certificate_url = http://push.example.com/event/subscribe/%(request_sha1sum)s | ||||||
|  |  | ||||||
| Restart the services: | Restart the services: | ||||||
|  |  | ||||||
| .. code:: bash | .. code:: bash | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ from pyasn1.codec.der import decoder | |||||||
| from datetime import datetime, date | from datetime import datetime, date | ||||||
| from jinja2 import Environment, PackageLoader, Template | from jinja2 import Environment, PackageLoader, Template | ||||||
|  |  | ||||||
|  | # TODO: Restrictive filesystem permissions result in TemplateNotFound exceptions | ||||||
| env = Environment(loader=PackageLoader("certidude", "templates")) | env = Environment(loader=PackageLoader("certidude", "templates")) | ||||||
|  |  | ||||||
| RE_HOSTNAME = "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$" | RE_HOSTNAME = "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$" | ||||||
| @@ -278,10 +279,9 @@ class RequestListResource(CertificateAuthorityBase): | |||||||
|  |  | ||||||
|         # Wait the certificate to be signed if waiting is requested |         # Wait the certificate to be signed if waiting is requested | ||||||
|         if req.get_param("wait"): |         if req.get_param("wait"): | ||||||
|             url_template = os.getenv("PUSH_SUBSCRIBE") |             if ca.subscribe_certificate_url: | ||||||
|             if url_template: |  | ||||||
|                 # Redirect to nginx pub/sub |                 # Redirect to nginx pub/sub | ||||||
|                 url = url_template % dict(channel=request.fingerprint()) |                 url = ca.subscribe_certificate_url % dict(request_sha1sum=request.fingerprint()) | ||||||
|                 click.echo("Redirecting to: %s"  % url) |                 click.echo("Redirecting to: %s"  % url) | ||||||
|                 resp.status = falcon.HTTP_SEE_OTHER |                 resp.status = falcon.HTTP_SEE_OTHER | ||||||
|                 resp.append_header("Location", url) |                 resp.append_header("Location", url) | ||||||
|   | |||||||
| @@ -489,7 +489,6 @@ def certidude_setup_strongswan_networkmanager(url, email_address, common_name, o | |||||||
|     default="/etc/uwsgi/apps-available/certidude.ini", |     default="/etc/uwsgi/apps-available/certidude.ini", | ||||||
|     type=click.File(mode="w", atomic=True, lazy=True), |     type=click.File(mode="w", atomic=True, lazy=True), | ||||||
|     help="uwsgi configuration, /etc/uwsgi/ by default") |     help="uwsgi configuration, /etc/uwsgi/ by default") | ||||||
| @click.option("--push-server", help="Push server URL, in case of different nginx instance") |  | ||||||
| def certidude_setup_production(username, hostname, push_server, nginx_config, uwsgi_config, static_path, kerberos_keytab): | def certidude_setup_production(username, hostname, push_server, nginx_config, uwsgi_config, static_path, kerberos_keytab): | ||||||
|     try: |     try: | ||||||
|         pwd.getpwnam(username) |         pwd.getpwnam(username) | ||||||
| @@ -540,8 +539,13 @@ def certidude_setup_production(username, hostname, push_server, nginx_config, uw | |||||||
| @click.option("--email-address", default=EMAIL, help="CA e-mail address") | @click.option("--email-address", default=EMAIL, help="CA e-mail address") | ||||||
| @click.option("--inbox", default="imap://user:pass@host:port/INBOX", help="Inbound e-mail server") | @click.option("--inbox", default="imap://user:pass@host:port/INBOX", help="Inbound e-mail server") | ||||||
| @click.option("--outbox", default="smtp://localhost", help="Outbound e-mail server") | @click.option("--outbox", default="smtp://localhost", help="Outbound e-mail server") | ||||||
|  | @click.option("--push-server", default="", help="Streaming nginx push server") | ||||||
| @click.argument("directory") | @click.argument("directory") | ||||||
| def certidude_setup_authority(parent, country, state, locality, organization, organizational_unit, common_name, directory, certificate_lifetime, authority_lifetime, revocation_list_lifetime, pkcs11, crl_distribution_url, ocsp_responder_url, email_address, inbox, outbox): | def certidude_setup_authority(parent, country, state, locality, organization, organizational_unit, common_name, directory, certificate_lifetime, authority_lifetime, revocation_list_lifetime, pkcs11, crl_distribution_url, ocsp_responder_url, email_address, inbox, outbox, push_server): | ||||||
|  |  | ||||||
|  |     publish_certificate_url = push_server + "/publish/%(request_sha1sum)s" | ||||||
|  |     subscribe_certificate_url = push_server + "/subscribe/%(request_sha1sum)s" | ||||||
|  |  | ||||||
|     slug = os.path.basename(directory[:-1] if directory.endswith('/') else directory) |     slug = os.path.basename(directory[:-1] if directory.endswith('/') else directory) | ||||||
|     if not slug: |     if not slug: | ||||||
|         raise click.ClickException("Please supply proper target path") |         raise click.ClickException("Please supply proper target path") | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								certidude/templates/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								certidude/templates/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -19,7 +19,7 @@ crlDistributionPoints = {{crl_distribution_points}} | |||||||
| emailAddress = {{email_address}} | emailAddress = {{email_address}} | ||||||
| {% endif %} | {% endif %} | ||||||
| x509_extensions = {{slug}}_cert | x509_extensions = {{slug}}_cert | ||||||
| policy = poliy_{{slug}} | policy = policy_{{slug}} | ||||||
|  |  | ||||||
| # Certidude specific stuff, TODO: move to separate section? | # Certidude specific stuff, TODO: move to separate section? | ||||||
| request_subnets = 10.0.0.0/8 192.168.0.0/16 172.168.0.0/16 | request_subnets = 10.0.0.0/8 192.168.0.0/16 172.168.0.0/16 | ||||||
| @@ -28,6 +28,8 @@ admin_subnets = 127.0.0.0/8 | |||||||
| admin_users = | admin_users = | ||||||
| inbox = {{inbox}} | inbox = {{inbox}} | ||||||
| outbox = {{outbox}} | outbox = {{outbox}} | ||||||
|  | publish_certificate_url = {{publish_certificate_url}} | ||||||
|  | subscribe_certificate_url = {{subscribe_certificate_url}} | ||||||
|  |  | ||||||
| [policy_{{slug}}] | [policy_{{slug}}] | ||||||
| countryName = match | countryName = match | ||||||
|   | |||||||
| @@ -12,13 +12,6 @@ callable = app | |||||||
| chmod-socket = 660 | chmod-socket = 660 | ||||||
| chown-socket = {{username}}:www-data | chown-socket = {{username}}:www-data | ||||||
| buffer-size = 32768 | buffer-size = 32768 | ||||||
| {% if push_server %} |  | ||||||
| env = PUSH_PUBLISH={{push_server}}/publish/%(channel)s |  | ||||||
| env = PUSH_SUBSCRIBE={{push_server}}/subscribe/%(channel)s |  | ||||||
| {% else %} |  | ||||||
| env = PUSH_PUBLISH=http://localhost/event/publish/%(channel)s |  | ||||||
| env = PUSH_SUBSCRIBE=http://localhost/event/subscribe/%(channel)s |  | ||||||
| {% endif %} |  | ||||||
| env = LANG=C.UTF-8 | env = LANG=C.UTF-8 | ||||||
| env = LC_ALL=C.UTF-8 | env = LC_ALL=C.UTF-8 | ||||||
| env = KRB5_KTNAME={{kerberos_keytab}} | env = KRB5_KTNAME={{kerberos_keytab}} | ||||||
|   | |||||||
| @@ -22,18 +22,18 @@ env = Environment(loader=PackageLoader("certidude", "email_templates")) | |||||||
| # https://jamielinux.com/docs/openssl-certificate-authority/ | # https://jamielinux.com/docs/openssl-certificate-authority/ | ||||||
| # http://pycopia.googlecode.com/svn/trunk/net/pycopia/ssl/certs.py | # http://pycopia.googlecode.com/svn/trunk/net/pycopia/ssl/certs.py | ||||||
|  |  | ||||||
| def notify(func): | def publish_certificate(func): | ||||||
|     # TODO: Implement e-mail and nginx notifications using hooks |     # TODO: Implement e-mail and nginx notifications using hooks | ||||||
|     def wrapped(instance, csr, *args, **kwargs): |     def wrapped(instance, csr, *args, **kwargs): | ||||||
|         cert = func(instance, csr, *args, **kwargs) |         cert = func(instance, csr, *args, **kwargs) | ||||||
|         assert isinstance(cert, Certificate), "notify wrapped function %s returned %s" % (func, type(cert)) |         assert isinstance(cert, Certificate), "notify wrapped function %s returned %s" % (func, type(cert)) | ||||||
|         url_template = os.getenv("PUSH_PUBLISH") |  | ||||||
|         if url_template: |         if instance.publish_certificate_url: | ||||||
|             url = url_template % dict(channel=csr.fingerprint()) |             url = instance.publish_certificate_url % dict(request_sha1sum=csr.fingerprint()) | ||||||
|             notification = urllib.request.Request(url, cert.dump().encode("ascii")) |             notification = urllib.request.Request(url, cert.dump().encode("ascii")) | ||||||
|             notification.add_header("User-Agent", "Certidude API") |             notification.add_header("User-Agent", "Certidude API") | ||||||
|             notification.add_header("Content-Type", "application/x-x509-user-cert") |             notification.add_header("Content-Type", "application/x-x509-user-cert") | ||||||
|             click.echo("Submitting notification to %s, waiting for response..." % url) |             click.echo("Publishing certificate at %s, waiting for response..." % url) | ||||||
|             response = urllib.request.urlopen(notification) |             response = urllib.request.urlopen(notification) | ||||||
|             response.read() |             response.read() | ||||||
|         return cert |         return cert | ||||||
| @@ -85,7 +85,7 @@ class CertificateAuthorityConfig(object): | |||||||
|         section = "CA_" + slug |         section = "CA_" + slug | ||||||
|  |  | ||||||
|         dirs = dict([(key, self.get(section, key)) |         dirs = dict([(key, self.get(section, key)) | ||||||
|             for key in ("dir", "certificate", "crl", "certs", "new_certs_dir", "private_key", "revoked_certs_dir", "request_subnets", "autosign_subnets", "admin_subnets", "admin_users")]) |             for key in ("dir", "certificate", "crl", "certs", "new_certs_dir", "private_key", "revoked_certs_dir", "request_subnets", "autosign_subnets", "admin_subnets", "admin_users", "publish_certificate_url", "subscribe_certificate_url", "inbox", "outbox")]) | ||||||
|  |  | ||||||
|         # Variable expansion, eg $dir |         # Variable expansion, eg $dir | ||||||
|         for key, value in dirs.items(): |         for key, value in dirs.items(): | ||||||
| @@ -94,8 +94,6 @@ class CertificateAuthorityConfig(object): | |||||||
|  |  | ||||||
|         dirs.pop("dir") |         dirs.pop("dir") | ||||||
|         dirs["email_address"] = self.get(section, "emailAddress") |         dirs["email_address"] = self.get(section, "emailAddress") | ||||||
|         dirs["inbox"] = self.get(section, "inbox") |  | ||||||
|         dirs["outbox"] = self.get(section, "outbox") |  | ||||||
|         dirs["certificate_lifetime"] = int(self.get(section, "default_days", "1825")) |         dirs["certificate_lifetime"] = int(self.get(section, "default_days", "1825")) | ||||||
|         dirs["revocation_list_lifetime"] = int(self.get(section, "default_crl_days", "1")) |         dirs["revocation_list_lifetime"] = int(self.get(section, "default_crl_days", "1")) | ||||||
|  |  | ||||||
| @@ -426,7 +424,7 @@ class Certificate(CertificateBase): | |||||||
|         return self.signed <= other.signed |         return self.signed <= other.signed | ||||||
|  |  | ||||||
| class CertificateAuthority(object): | class CertificateAuthority(object): | ||||||
|     def __init__(self, slug, certificate, crl, certs, new_certs_dir, revoked_certs_dir=None, private_key=None, autosign_subnets=None, request_subnets=None, admin_subnets=None, admin_users=None, email_address=None, inbox=None, outbox=None, basic_constraints="CA:FALSE", key_usage="digitalSignature,keyEncipherment", extended_key_usage="clientAuth", certificate_lifetime=5*365, revocation_list_lifetime=1): |     def __init__(self, slug, certificate, crl, certs, new_certs_dir, revoked_certs_dir=None, private_key=None, autosign_subnets=None, request_subnets=None, admin_subnets=None, admin_users=None, email_address=None, inbox=None, outbox=None, basic_constraints="CA:FALSE", key_usage="digitalSignature,keyEncipherment", extended_key_usage="clientAuth", certificate_lifetime=5*365, revocation_list_lifetime=1, publish_certificate_url=None, subscribe_certificate_url=None): | ||||||
|         self.slug = slug |         self.slug = slug | ||||||
|         self.revocation_list = crl |         self.revocation_list = crl | ||||||
|         self.signed_dir = certs |         self.signed_dir = certs | ||||||
| @@ -440,6 +438,9 @@ class CertificateAuthority(object): | |||||||
|  |  | ||||||
|         self.certificate = Certificate(open(certificate)) |         self.certificate = Certificate(open(certificate)) | ||||||
|         self.mailer = Mailer(outbox) if outbox else None |         self.mailer = Mailer(outbox) if outbox else None | ||||||
|  |         self.publish_certificate_url = publish_certificate_url | ||||||
|  |         self.subscribe_certificate_url = subscribe_certificate_url | ||||||
|  |  | ||||||
|         self.certificate_lifetime = certificate_lifetime |         self.certificate_lifetime = certificate_lifetime | ||||||
|         self.revocation_list_lifetime = revocation_list_lifetime |         self.revocation_list_lifetime = revocation_list_lifetime | ||||||
|         self.basic_constraints = basic_constraints |         self.basic_constraints = basic_constraints | ||||||
| @@ -546,7 +547,7 @@ class CertificateAuthority(object): | |||||||
|         return crypto.dump_privatekey(crypto.FILETYPE_PEM, key).decode("ascii"), \ |         return crypto.dump_privatekey(crypto.FILETYPE_PEM, key).decode("ascii"), \ | ||||||
|             req_buf, cert_buf |             req_buf, cert_buf | ||||||
|  |  | ||||||
|     @notify |     @publish_certificate | ||||||
|     def sign(self, req, overwrite=False, delete=True): |     def sign(self, req, overwrite=False, delete=True): | ||||||
|         """ |         """ | ||||||
|         Sign certificate signing request via signer process |         Sign certificate signing request via signer process | ||||||
| @@ -572,7 +573,7 @@ class CertificateAuthority(object): | |||||||
|  |  | ||||||
|         return Certificate(open(cert_path)) |         return Certificate(open(cert_path)) | ||||||
|  |  | ||||||
|     @notify |     @publish_certificate | ||||||
|     def sign2(self, request, overwrite=False, delete=True, lifetime=None): |     def sign2(self, request, overwrite=False, delete=True, lifetime=None): | ||||||
|         """ |         """ | ||||||
|         Sign directly using private key, this is usually done by root. |         Sign directly using private key, this is usually done by root. | ||||||
|   | |||||||
| @@ -8,7 +8,5 @@ import os | |||||||
| from certidude.api import certidude_app | from certidude.api import certidude_app | ||||||
|  |  | ||||||
| # TODO: set up /run/certidude/api paths and permissions | # TODO: set up /run/certidude/api paths and permissions | ||||||
| assert os.getenv("PUSH_SUBSCRIBE"), "Please set PUSH_SUBSCRIBE to your web server's subscription URL" |  | ||||||
| assert os.getenv("PUSH_PUBLISH"), "Please set PUSH_PUBLISH to your web server's publishing URL" |  | ||||||
|  |  | ||||||
| app = certidude_app() | app = certidude_app() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user