mirror of
				https://github.com/laurivosandi/certidude
				synced 2025-10-30 17:09:19 +00:00 
			
		
		
		
	| @@ -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 | ||||||
		Reference in New Issue
	
	Block a user