mirror of
https://github.com/laurivosandi/certidude
synced 2024-12-23 00:25:18 +00:00
Merge branch 'master' of github.com:laurivosandi/certidude
This commit is contained in:
commit
f1c0a3925d
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user