mirror of
https://github.com/laurivosandi/certidude
synced 2024-12-23 00:25:18 +00:00
commit
70f89f996a
@ -60,7 +60,7 @@ To install Certidude:
|
|||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
apt-get install -y python3 python3-pip python3-dev cython3 build-essential libffi-dev libssl-dev
|
apt-get install -y python3 python3-pip python3-dev cython3 build-essential libffi-dev libssl-dev libkrb5-dev
|
||||||
pip3 install certidude
|
pip3 install certidude
|
||||||
|
|
||||||
Make sure you're running PyOpenSSL 0.15+ and netifaces 0.10.4+ from PyPI,
|
Make sure you're running PyOpenSSL 0.15+ and netifaces 0.10.4+ from PyPI,
|
||||||
|
@ -9,18 +9,15 @@ import mimetypes
|
|||||||
import netifaces
|
import netifaces
|
||||||
import os
|
import os
|
||||||
import pwd
|
import pwd
|
||||||
import random
|
|
||||||
import re
|
import re
|
||||||
import signal
|
import signal
|
||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
|
||||||
from certidude.helpers import expand_paths, \
|
from certidude.helpers import expand_paths, \
|
||||||
certidude_request_certificate
|
certidude_request_certificate
|
||||||
from certidude.signer import SignServer
|
from certidude.signer import SignServer
|
||||||
from certidude.wrappers import CertificateAuthorityConfig, \
|
from certidude.wrappers import CertificateAuthorityConfig, subject2dn
|
||||||
CertificateAuthority, Certificate, subject2dn, Request
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from humanize import naturaltime
|
from humanize import naturaltime
|
||||||
from ipaddress import ip_network
|
from ipaddress import ip_network
|
||||||
@ -46,7 +43,7 @@ assert hasattr(crypto.X509Req(), "get_extensions"), "You're running too old vers
|
|||||||
# keyUsage, extendedKeyUsage - https://www.openssl.org/docs/apps/x509v3_config.html
|
# keyUsage, extendedKeyUsage - https://www.openssl.org/docs/apps/x509v3_config.html
|
||||||
# strongSwan key paths - https://wiki.strongswan.org/projects/1/wiki/SimpleCA
|
# strongSwan key paths - https://wiki.strongswan.org/projects/1/wiki/SimpleCA
|
||||||
|
|
||||||
config = CertificateAuthorityConfig("/etc/ssl/openssl.cnf")
|
config = CertificateAuthorityConfig()
|
||||||
|
|
||||||
# Parse command-line argument defaults from environment
|
# Parse command-line argument defaults from environment
|
||||||
HOSTNAME = socket.gethostname()
|
HOSTNAME = socket.gethostname()
|
||||||
@ -77,6 +74,11 @@ def certidude_spawn(kill, no_interaction):
|
|||||||
"""
|
"""
|
||||||
Spawn processes for signers
|
Spawn processes for signers
|
||||||
"""
|
"""
|
||||||
|
# Check whether we have privileges
|
||||||
|
os.umask(0o027)
|
||||||
|
uid = os.getuid()
|
||||||
|
if uid != 0:
|
||||||
|
raise click.ClickException("Not running as root")
|
||||||
|
|
||||||
# Process directories
|
# Process directories
|
||||||
run_dir = "/run/certidude"
|
run_dir = "/run/certidude"
|
||||||
@ -88,10 +90,6 @@ def certidude_spawn(kill, no_interaction):
|
|||||||
click.echo("Creating: %s" % signer_dir)
|
click.echo("Creating: %s" % signer_dir)
|
||||||
os.makedirs(signer_dir)
|
os.makedirs(signer_dir)
|
||||||
|
|
||||||
os.umask(0o027)
|
|
||||||
uid = os.getuid()
|
|
||||||
assert uid == 0, "Not running as root"
|
|
||||||
|
|
||||||
# Preload charmap encoding for byte_string() function of pyOpenSSL
|
# Preload charmap encoding for byte_string() function of pyOpenSSL
|
||||||
# in order to enable chrooting
|
# in order to enable chrooting
|
||||||
"".encode("charmap")
|
"".encode("charmap")
|
||||||
@ -103,6 +101,7 @@ def certidude_spawn(kill, no_interaction):
|
|||||||
# TODO: use os.mknod instead
|
# TODO: use os.mknod instead
|
||||||
os.system("mknod -m 444 %s c 1 9" % os.path.join(chroot_dir, "dev", "urandom"))
|
os.system("mknod -m 444 %s c 1 9" % os.path.join(chroot_dir, "dev", "urandom"))
|
||||||
|
|
||||||
|
ca_loaded = False
|
||||||
for ca in config.all_authorities():
|
for ca in config.all_authorities():
|
||||||
socket_path = os.path.join(signer_dir, ca.slug + ".sock")
|
socket_path = os.path.join(signer_dir, ca.slug + ".sock")
|
||||||
pidfile_path = os.path.join(signer_dir, ca.slug + ".pid")
|
pidfile_path = os.path.join(signer_dir, ca.slug + ".pid")
|
||||||
@ -125,7 +124,9 @@ def certidude_spawn(kill, no_interaction):
|
|||||||
sleep(1)
|
sleep(1)
|
||||||
except ProcessLookupError:
|
except ProcessLookupError:
|
||||||
pass
|
pass
|
||||||
|
ca_loaded = True
|
||||||
else:
|
else:
|
||||||
|
ca_loaded = True
|
||||||
continue
|
continue
|
||||||
|
|
||||||
child_pid = os.fork()
|
child_pid = os.fork()
|
||||||
@ -144,6 +145,10 @@ def certidude_spawn(kill, no_interaction):
|
|||||||
asyncore.loop()
|
asyncore.loop()
|
||||||
else:
|
else:
|
||||||
click.echo("Spawned certidude signer process with PID %d at %s" % (child_pid, socket_path))
|
click.echo("Spawned certidude signer process with PID %d at %s" % (child_pid, socket_path))
|
||||||
|
ca_loaded = True
|
||||||
|
|
||||||
|
if not ca_loaded:
|
||||||
|
raise click.ClickException("No CA sections defined in configuration: {}".format(config.path))
|
||||||
|
|
||||||
|
|
||||||
@click.command("client", help="Setup X.509 certificates for application")
|
@click.command("client", help="Setup X.509 certificates for application")
|
||||||
@ -469,6 +474,12 @@ def certidude_setup_authority(parent, country, state, locality, organization, or
|
|||||||
_, _, uid, gid, gecos, root, shell = pwd.getpwnam(group)
|
_, _, uid, gid, gecos, root, shell = pwd.getpwnam(group)
|
||||||
os.setgid(gid)
|
os.setgid(gid)
|
||||||
|
|
||||||
|
slug = os.path.basename(directory[:-1] if directory.endswith('/') else directory)
|
||||||
|
if not slug:
|
||||||
|
raise ValueError("Please supply proper target path")
|
||||||
|
|
||||||
|
click.echo("CA configuration files are saved to: {}".format(os.path.abspath(slug)))
|
||||||
|
|
||||||
click.echo("Generating 4096-bit RSA key...")
|
click.echo("Generating 4096-bit RSA key...")
|
||||||
|
|
||||||
if pkcs11:
|
if pkcs11:
|
||||||
@ -477,8 +488,6 @@ def certidude_setup_authority(parent, country, state, locality, organization, or
|
|||||||
key = crypto.PKey()
|
key = crypto.PKey()
|
||||||
key.generate_key(crypto.TYPE_RSA, 4096)
|
key.generate_key(crypto.TYPE_RSA, 4096)
|
||||||
|
|
||||||
slug = os.path.basename(directory)
|
|
||||||
|
|
||||||
if not crl_distribution_url:
|
if not crl_distribution_url:
|
||||||
crl_distribution_url = "http://%s/api/%s/revoked/" % (common_name, slug)
|
crl_distribution_url = "http://%s/api/%s/revoked/" % (common_name, slug)
|
||||||
|
|
||||||
@ -575,9 +584,13 @@ def certidude_setup_authority(parent, country, state, locality, organization, or
|
|||||||
with open(ca_key, "wb") as fh:
|
with open(ca_key, "wb") as fh:
|
||||||
fh.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, key))
|
fh.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, key))
|
||||||
|
|
||||||
click.echo("Insert following to /etc/ssl/openssl.cnf:")
|
with open(os.path.join(directory, "openssl.cnf.example"), "w") as fh:
|
||||||
|
fh.write(env.get_template("openssl.cnf").render(locals()))
|
||||||
|
|
||||||
|
click.echo("You need to copy the contents of the 'openssl.cnf.example'")
|
||||||
|
click.echo("to system-wide OpenSSL configuration file, usually located")
|
||||||
|
click.echo("at /etc/ssl/openssl.cnf")
|
||||||
click.echo()
|
click.echo()
|
||||||
click.secho(env.get_template("openssl.cnf").render(locals()), fg="blue")
|
|
||||||
|
|
||||||
click.echo()
|
click.echo()
|
||||||
click.echo("Use following commands to inspect the newly created files:")
|
click.echo("Use following commands to inspect the newly created files:")
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
# You have to copy the settings to the system-wide
|
||||||
|
# OpenSSL configuration (usually /etc/ssl/openssl.cnf
|
||||||
|
|
||||||
[CA_{{slug}}]
|
[CA_{{slug}}]
|
||||||
default_crl_days = {{revocation_list_lifetime}}
|
default_crl_days = {{revocation_list_lifetime}}
|
||||||
default_days = {{certificate_lifetime}}
|
default_days = {{certificate_lifetime}}
|
||||||
@ -38,3 +41,4 @@ emailAddress = optional
|
|||||||
basicConstraints = CA:FALSE
|
basicConstraints = CA:FALSE
|
||||||
keyUsage = nonRepudiation,digitalSignature,keyEncipherment
|
keyUsage = nonRepudiation,digitalSignature,keyEncipherment
|
||||||
extendedKeyUsage = clientAuth
|
extendedKeyUsage = clientAuth
|
||||||
|
|
||||||
|
@ -61,13 +61,19 @@ def subject2dn(subject):
|
|||||||
|
|
||||||
class CertificateAuthorityConfig(object):
|
class CertificateAuthorityConfig(object):
|
||||||
"""
|
"""
|
||||||
Attempt to parse CA-s from openssl.cnf
|
Certificate Authority configuration
|
||||||
|
|
||||||
|
:param path: Absolute path to configuration file.
|
||||||
|
Defaults to /etc/ssl/openssl.cnf
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, path='/etc/ssl/openssl.cnf', *args):
|
||||||
|
|
||||||
|
#: Path to file where current configuration is loaded from.
|
||||||
|
self.path = path
|
||||||
|
|
||||||
self._config = RawConfigParser()
|
self._config = RawConfigParser()
|
||||||
for arg in args:
|
self._config.readfp(itertools.chain(["[global]"], open(self.path)))
|
||||||
self._config.readfp(itertools.chain(["[global]"], open(os.path.expanduser(arg))))
|
|
||||||
|
|
||||||
def get(self, section, key, default=""):
|
def get(self, section, key, default=""):
|
||||||
if self._config.has_option(section, key):
|
if self._config.has_option(section, key):
|
||||||
|
@ -11,7 +11,7 @@ from certidude.api import CertificateAuthorityResource, \
|
|||||||
# TODO: deduplicate routing code
|
# TODO: deduplicate routing code
|
||||||
# TODO: set up /run/certidude/api paths and permissions
|
# TODO: set up /run/certidude/api paths and permissions
|
||||||
|
|
||||||
config = CertificateAuthorityConfig("/etc/ssl/openssl.cnf")
|
config = CertificateAuthorityConfig()
|
||||||
|
|
||||||
assert os.getenv("PUSH_SUBSCRIBE"), "Please set PUSH_SUBSCRIBE to your web server's subscription URL"
|
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"
|
assert os.getenv("PUSH_PUBLISH"), "Please set PUSH_PUBLISH to your web server's publishing URL"
|
||||||
|
19
requirements.txt
Normal file
19
requirements.txt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
cffi==1.2.1
|
||||||
|
click==5.1
|
||||||
|
cryptography==1.0
|
||||||
|
falcon==0.3.0
|
||||||
|
humanize==0.5.1
|
||||||
|
idna==2.0
|
||||||
|
Jinja2==2.8
|
||||||
|
ldap3==0.9.8.8
|
||||||
|
MarkupSafe==0.23
|
||||||
|
netifaces==0.10.4
|
||||||
|
pyasn1==0.1.8
|
||||||
|
pycountry==1.14
|
||||||
|
pycparser==2.14
|
||||||
|
pycrypto==2.6.1
|
||||||
|
pykerberos==1.1.8
|
||||||
|
pyOpenSSL==0.15.1
|
||||||
|
python-mimeparse==0.1.4
|
||||||
|
setproctitle==1.1.9
|
||||||
|
six==1.9.0
|
Loading…
Reference in New Issue
Block a user