diff --git a/README.rst b/README.rst index 4bc0f0c..17d12fc 100644 --- a/README.rst +++ b/README.rst @@ -154,8 +154,6 @@ Otherwise manually configure ``uwsgi`` application in ``/etc/uwsgi/apps-availabl chmod-socket = 660 chown-socket = certidude:www-data 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 = LC_ALL=C.UTF-8 env = KRB5_KTNAME=/etc/certidude.keytab @@ -232,6 +230,11 @@ Also adjust ``/etc/nginx/nginx.conf``: 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: .. code:: bash diff --git a/certidude/api.py b/certidude/api.py index 70a98f4..e322d31 100644 --- a/certidude/api.py +++ b/certidude/api.py @@ -13,6 +13,7 @@ from pyasn1.codec.der import decoder from datetime import datetime, date from jinja2 import Environment, PackageLoader, Template +# TODO: Restrictive filesystem permissions result in TemplateNotFound exceptions 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])$" @@ -278,10 +279,9 @@ class RequestListResource(CertificateAuthorityBase): # Wait the certificate to be signed if waiting is requested if req.get_param("wait"): - url_template = os.getenv("PUSH_SUBSCRIBE") - if url_template: + if ca.subscribe_certificate_url: # 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) resp.status = falcon.HTTP_SEE_OTHER resp.append_header("Location", url) diff --git a/certidude/cli.py b/certidude/cli.py index cc1fb96..9dde6d8 100755 --- a/certidude/cli.py +++ b/certidude/cli.py @@ -489,7 +489,6 @@ def certidude_setup_strongswan_networkmanager(url, email_address, common_name, o default="/etc/uwsgi/apps-available/certidude.ini", type=click.File(mode="w", atomic=True, lazy=True), 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): try: 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("--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("--push-server", default="", help="Streaming nginx push server") @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) if not slug: raise click.ClickException("Please supply proper target path") diff --git a/certidude/templates/__init__.py b/certidude/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/certidude/templates/openssl.cnf b/certidude/templates/openssl.cnf index 6b09e10..a1524f8 100644 --- a/certidude/templates/openssl.cnf +++ b/certidude/templates/openssl.cnf @@ -19,7 +19,7 @@ crlDistributionPoints = {{crl_distribution_points}} emailAddress = {{email_address}} {% endif %} x509_extensions = {{slug}}_cert -policy = poliy_{{slug}} +policy = policy_{{slug}} # 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 @@ -28,6 +28,8 @@ admin_subnets = 127.0.0.0/8 admin_users = inbox = {{inbox}} outbox = {{outbox}} +publish_certificate_url = {{publish_certificate_url}} +subscribe_certificate_url = {{subscribe_certificate_url}} [policy_{{slug}}] countryName = match diff --git a/certidude/templates/uwsgi.ini b/certidude/templates/uwsgi.ini index de2809e..d07b570 100644 --- a/certidude/templates/uwsgi.ini +++ b/certidude/templates/uwsgi.ini @@ -12,13 +12,6 @@ callable = app chmod-socket = 660 chown-socket = {{username}}:www-data 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 = LC_ALL=C.UTF-8 env = KRB5_KTNAME={{kerberos_keytab}} diff --git a/certidude/wrappers.py b/certidude/wrappers.py index fcbdbcc..3c42886 100644 --- a/certidude/wrappers.py +++ b/certidude/wrappers.py @@ -22,18 +22,18 @@ env = Environment(loader=PackageLoader("certidude", "email_templates")) # https://jamielinux.com/docs/openssl-certificate-authority/ # 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 def wrapped(instance, csr, *args, **kwargs): cert = func(instance, csr, *args, **kwargs) assert isinstance(cert, Certificate), "notify wrapped function %s returned %s" % (func, type(cert)) - url_template = os.getenv("PUSH_PUBLISH") - if url_template: - url = url_template % dict(channel=csr.fingerprint()) + + if instance.publish_certificate_url: + url = instance.publish_certificate_url % dict(request_sha1sum=csr.fingerprint()) notification = urllib.request.Request(url, cert.dump().encode("ascii")) notification.add_header("User-Agent", "Certidude API") 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.read() return cert @@ -85,7 +85,7 @@ class CertificateAuthorityConfig(object): section = "CA_" + slug 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 for key, value in dirs.items(): @@ -94,8 +94,6 @@ class CertificateAuthorityConfig(object): dirs.pop("dir") 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["revocation_list_lifetime"] = int(self.get(section, "default_crl_days", "1")) @@ -426,7 +424,7 @@ class Certificate(CertificateBase): return self.signed <= other.signed 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.revocation_list = crl self.signed_dir = certs @@ -440,6 +438,9 @@ class CertificateAuthority(object): self.certificate = Certificate(open(certificate)) 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.revocation_list_lifetime = revocation_list_lifetime self.basic_constraints = basic_constraints @@ -546,7 +547,7 @@ class CertificateAuthority(object): return crypto.dump_privatekey(crypto.FILETYPE_PEM, key).decode("ascii"), \ req_buf, cert_buf - @notify + @publish_certificate def sign(self, req, overwrite=False, delete=True): """ Sign certificate signing request via signer process @@ -572,7 +573,7 @@ class CertificateAuthority(object): return Certificate(open(cert_path)) - @notify + @publish_certificate def sign2(self, request, overwrite=False, delete=True, lifetime=None): """ Sign directly using private key, this is usually done by root. diff --git a/certidude/wsgi.py b/certidude/wsgi.py index 5b546c4..9f93e8f 100644 --- a/certidude/wsgi.py +++ b/certidude/wsgi.py @@ -8,7 +8,5 @@ import os from certidude.api import certidude_app # 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()