mirror of
				https://github.com/laurivosandi/certidude
				synced 2025-10-31 09:29:13 +00:00 
			
		
		
		
	Refactor signature request submission
Certidude client now reads configuration from /etc/certidude/client.conf, submits CSR-s and once signed configures services based on /etc/certidude/services.conf
This commit is contained in:
		| @@ -8,6 +8,7 @@ from OpenSSL import crypto | |||||||
| from certidude import config, push | from certidude import config, push | ||||||
| from certidude.wrappers import Certificate, Request | from certidude.wrappers import Certificate, Request | ||||||
| from certidude.signer import raw_sign | from certidude.signer import raw_sign | ||||||
|  | from certidude import errors | ||||||
|  |  | ||||||
| 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])$" | ||||||
|  |  | ||||||
| @@ -15,12 +16,6 @@ RE_HOSTNAME = "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0 | |||||||
| # 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 | ||||||
|  |  | ||||||
| class RequestExists(Exception): |  | ||||||
|     pass |  | ||||||
|  |  | ||||||
| class DuplicateCommonNameError(Exception): |  | ||||||
|     pass |  | ||||||
|  |  | ||||||
| def publish_certificate(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(csr, *args, **kwargs): |     def wrapped(csr, *args, **kwargs): | ||||||
| @@ -68,9 +63,9 @@ def store_request(buf, overwrite=False): | |||||||
|     # If there is cert, check if it's the same |     # If there is cert, check if it's the same | ||||||
|     if os.path.exists(request_path): |     if os.path.exists(request_path): | ||||||
|         if open(request_path).read() == buf: |         if open(request_path).read() == buf: | ||||||
|             raise RequestExists("Request already exists") |             raise errors.RequestExists("Request already exists") | ||||||
|         else: |         else: | ||||||
|             raise DuplicateCommonNameError("Another request with same common name already exists") |             raise errors.DuplicateCommonNameError("Another request with same common name already exists") | ||||||
|     else: |     else: | ||||||
|         with open(request_path + ".part", "w") as fh: |         with open(request_path + ".part", "w") as fh: | ||||||
|             fh.write(buf) |             fh.write(buf) | ||||||
|   | |||||||
							
								
								
									
										179
									
								
								certidude/cli.py
									
									
									
									
									
								
							
							
						
						
									
										179
									
								
								certidude/cli.py
									
									
									
									
									
								
							| @@ -9,6 +9,7 @@ import logging | |||||||
| import os | import os | ||||||
| import pwd | import pwd | ||||||
| import re | import re | ||||||
|  | import requests | ||||||
| import signal | import signal | ||||||
| import socket | import socket | ||||||
| import subprocess | import subprocess | ||||||
| @@ -23,6 +24,7 @@ from time import sleep | |||||||
| from setproctitle import setproctitle | from setproctitle import setproctitle | ||||||
| from OpenSSL import crypto | from OpenSSL import crypto | ||||||
|  |  | ||||||
|  |  | ||||||
| env = Environment(loader=PackageLoader("certidude", "templates"), trim_blocks=True) | env = Environment(loader=PackageLoader("certidude", "templates"), trim_blocks=True) | ||||||
|  |  | ||||||
| # Big fat warning: | # Big fat warning: | ||||||
| @@ -60,12 +62,162 @@ if os.getuid() >= 1000: | |||||||
|         FIRST_NAME = gecos |         FIRST_NAME = gecos | ||||||
|  |  | ||||||
|  |  | ||||||
| @click.command("spawn", help="Run privilege isolated signer process") | @click.command("request", help="Run processes for requesting certificates and configuring services") | ||||||
|  | @click.option("-f", "--fork", default=False, is_flag=True, help="Fork to background") | ||||||
|  | def certidude_spawn_request(fork): | ||||||
|  |     from certidude.helpers import certidude_request_certificate | ||||||
|  |     from configparser import ConfigParser | ||||||
|  |  | ||||||
|  |     clients = ConfigParser() | ||||||
|  |     clients.readfp(open("/etc/certidude/client.conf")) | ||||||
|  |  | ||||||
|  |     services = ConfigParser() | ||||||
|  |     services.readfp(open("/etc/certidude/services.conf")) | ||||||
|  |  | ||||||
|  |     # Process directories | ||||||
|  |     run_dir = "/run/certidude" | ||||||
|  |  | ||||||
|  |     # Prepare signer PID-s directory | ||||||
|  |     if not os.path.exists(run_dir): | ||||||
|  |         click.echo("Creating: %s" % run_dir) | ||||||
|  |         os.makedirs(run_dir) | ||||||
|  |  | ||||||
|  |     for certificate in clients.sections(): | ||||||
|  |         if clients.get(certificate, "managed") != "true": | ||||||
|  |             continue | ||||||
|  |  | ||||||
|  |         pid_path = os.path.join(run_dir, certificate + ".pid") | ||||||
|  |  | ||||||
|  |         try: | ||||||
|  |             with open(pid_path) as fh: | ||||||
|  |                 pid = int(fh.readline()) | ||||||
|  |                 os.kill(pid, signal.SIGTERM) | ||||||
|  |                 click.echo("Terminated process %d" % pid) | ||||||
|  |             os.unlink(pid_path) | ||||||
|  |         except (ValueError, ProcessLookupError, FileNotFoundError): | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |         if fork: | ||||||
|  |             child_pid = os.fork() | ||||||
|  |         else: | ||||||
|  |             child_pid = None | ||||||
|  |  | ||||||
|  |         if child_pid: | ||||||
|  |             click.echo("Spawned certificate request process with PID %d" % (child_pid)) | ||||||
|  |             continue | ||||||
|  |  | ||||||
|  |         with open(pid_path, "w") as fh: | ||||||
|  |             fh.write("%d\n" % os.getpid()) | ||||||
|  |         setproctitle("certidude spawn request %s" % certificate) | ||||||
|  |         retries = 30 | ||||||
|  |         while retries > 0: | ||||||
|  |             try: | ||||||
|  |                 certidude_request_certificate( | ||||||
|  |                     clients.get(certificate, "server"), | ||||||
|  |                     clients.get(certificate, "key_path"), | ||||||
|  |                     clients.get(certificate, "request_path"), | ||||||
|  |                     clients.get(certificate, "certificate_path"), | ||||||
|  |                     clients.get(certificate, "authority_path"), | ||||||
|  |                     socket.gethostname(), | ||||||
|  |                     None, | ||||||
|  |                     autosign=True, | ||||||
|  |                     wait=True) | ||||||
|  |                 break | ||||||
|  |             except requests.exceptions.Timeout: | ||||||
|  |                 retries -= 1 | ||||||
|  |                 continue | ||||||
|  |  | ||||||
|  |         for endpoint in services.sections(): | ||||||
|  |             if services.get(endpoint, "certificate") != certificate: | ||||||
|  |                 continue | ||||||
|  |  | ||||||
|  |             csummer = hashlib.sha1() | ||||||
|  |             csummer.update(endpoint.encode("ascii")) | ||||||
|  |             csum = csummer.hexdigest() | ||||||
|  |             uuid = csum[:8] + "-" + csum[8:12] + "-" + csum[12:16] + "-" + csum[16:20] + "-" + csum[20:32] | ||||||
|  |  | ||||||
|  |             # Set up IPsec via NetworkManager | ||||||
|  |             if services.get(endpoint, "service") == "network-manager/strongswan": | ||||||
|  |  | ||||||
|  |                 config = configparser.ConfigParser() | ||||||
|  |                 config.add_section("connection") | ||||||
|  |                 config.add_section("vpn") | ||||||
|  |                 config.add_section("ipv4") | ||||||
|  |  | ||||||
|  |                 config.set("connection", "id", endpoint) | ||||||
|  |                 config.set("connection", "uuid", uuid) | ||||||
|  |                 config.set("connection", "type", "vpn") | ||||||
|  |  | ||||||
|  |                 config.set("vpn", "service-type", "org.freedesktop.NetworkManager.strongswan") | ||||||
|  |                 config.set("vpn", "userkey", clients.get(certificate, "key_path")) | ||||||
|  |                 config.set("vpn", "usercert", clients.get(certificate, "certificate_path")) | ||||||
|  |                 config.set("vpn", "encap", "no") | ||||||
|  |                 config.set("vpn", "address", services.get(endpoint, "remote")) | ||||||
|  |                 config.set("vpn", "virtual", "yes") | ||||||
|  |                 config.set("vpn", "method", "key") | ||||||
|  |                 config.set("vpn", "certificate", clients.get(certificate, "authority_path")) | ||||||
|  |                 config.set("vpn", "ipcomp", "no") | ||||||
|  |  | ||||||
|  |                 config.set("ipv4", "method", "auto") | ||||||
|  |  | ||||||
|  |                 # Add routes, may need some more tweaking | ||||||
|  |                 for index, subnet in enumerate(services.get(endpoint, "route").split(","), start=1): | ||||||
|  |                     config.set("ipv4", "route%d" % index, subnet) | ||||||
|  |  | ||||||
|  |                 # Prevent creation of files with liberal permissions | ||||||
|  |                 os.umask(0o177) | ||||||
|  |  | ||||||
|  |                 # Write keyfile | ||||||
|  |                 with open(os.path.join("/etc/NetworkManager/system-connections", endpoint), "w") as configfile: | ||||||
|  |                     config.write(configfile) | ||||||
|  |                 continue | ||||||
|  |  | ||||||
|  |             # Set up IPsec via /etc/ipsec.conf | ||||||
|  |             if services.get(endpoint, "service") == "strongswan": | ||||||
|  |                 from ipsecparse import loads | ||||||
|  |                 config = loads(open('/etc/ipsec.conf').read()) | ||||||
|  |                 config["conn", endpoint] = dict( | ||||||
|  |                     leftsourceip="%config", | ||||||
|  |                     left="%defaultroute", | ||||||
|  |                     leftcert=clients.get(certificate, "certificate_path"), | ||||||
|  |                     rightid="%any", | ||||||
|  |                     right=services.get(endpoint, "remote"), | ||||||
|  |                     rightsubnet=services.get(endpoint, "route"), | ||||||
|  |                     keyexchange="ikev2", | ||||||
|  |                     keyingtries="300", | ||||||
|  |                     dpdaction="restart", | ||||||
|  |                     closeaction="restart", | ||||||
|  |                     auto="start") | ||||||
|  |                 with open("/etc/ipsec.conf.part", "w") as fh: | ||||||
|  |                     fh.write(config.dumps()) | ||||||
|  |                 os.rename("/etc/ipsec.conf.part", "/etc/ipsec.conf") | ||||||
|  |  | ||||||
|  |                 # Regenerate /etc/ipsec.secrets | ||||||
|  |                 with open("/etc/ipsec.secrets.part", "w") as fh: | ||||||
|  |                     for filename in os.listdir("/etc/ipsec.d/private"): | ||||||
|  |                         if not filename.endswith(".pem"): | ||||||
|  |                             continue | ||||||
|  |                         fh.write(": RSA /etc/ipsec.d/private/%s\n" % filename) | ||||||
|  |                 os.rename("/etc/ipsec.secrets.part", "/etc/ipsec.secrets") | ||||||
|  |  | ||||||
|  |                 # Attempt to reload config or start if it's not running | ||||||
|  |                 if os.system("ipsec update") == 130: | ||||||
|  |                     os.system("ipsec start") | ||||||
|  |                 continue | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             # TODO: OpenVPN, Puppet, OpenLDAP, intranet HTTPS, <insert awesomeness here> | ||||||
|  |  | ||||||
|  |         os.unlink(pid_path) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @click.command("signer", help="Run privilege isolated signer process") | ||||||
| @click.option("-k", "--kill", default=False, is_flag=True, help="Kill previous instance") | @click.option("-k", "--kill", default=False, is_flag=True, help="Kill previous instance") | ||||||
| @click.option("-n", "--no-interaction", default=True, is_flag=True, help="Don't load password protected keys") | @click.option("-n", "--no-interaction", default=True, is_flag=True, help="Don't load password protected keys") | ||||||
| def certidude_spawn(kill, no_interaction): | def certidude_spawn_signer(kill, no_interaction): | ||||||
|     """ |     """ | ||||||
|     Spawn processes for signers |     Spawn privilege isolated signer process | ||||||
|     """ |     """ | ||||||
|     from certidude import config |     from certidude import config | ||||||
|  |  | ||||||
| @@ -80,7 +232,6 @@ def certidude_spawn(kill, no_interaction): | |||||||
|  |  | ||||||
|     # Process directories |     # Process directories | ||||||
|     run_dir = "/run/certidude" |     run_dir = "/run/certidude" | ||||||
|     chroot_dir = os.path.join(run_dir, "jail") |  | ||||||
|  |  | ||||||
|     # Prepare signer PID-s directory |     # Prepare signer PID-s directory | ||||||
|     if not os.path.exists(run_dir): |     if not os.path.exists(run_dir): | ||||||
| @@ -92,15 +243,13 @@ def certidude_spawn(kill, no_interaction): | |||||||
|     "".encode("charmap") |     "".encode("charmap") | ||||||
|  |  | ||||||
|     # Prepare chroot directories |     # Prepare chroot directories | ||||||
|  |     chroot_dir = os.path.join(run_dir, "jail") | ||||||
|     if not os.path.exists(os.path.join(chroot_dir, "dev")): |     if not os.path.exists(os.path.join(chroot_dir, "dev")): | ||||||
|         os.makedirs(os.path.join(chroot_dir, "dev")) |         os.makedirs(os.path.join(chroot_dir, "dev")) | ||||||
|     if not os.path.exists(os.path.join(chroot_dir, "dev", "urandom")): |     if not os.path.exists(os.path.join(chroot_dir, "dev", "urandom")): | ||||||
|         # 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 |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     try: |     try: | ||||||
|         with open(config.SIGNER_PID_PATH) as fh: |         with open(config.SIGNER_PID_PATH) as fh: | ||||||
|             pid = int(fh.readline()) |             pid = int(fh.readline()) | ||||||
| @@ -122,11 +271,13 @@ def certidude_spawn(kill, no_interaction): | |||||||
|  |  | ||||||
|     child_pid = os.fork() |     child_pid = os.fork() | ||||||
|  |  | ||||||
|     if child_pid == 0: |     if child_pid: | ||||||
|  |         click.echo("Spawned certidude signer process with PID %d at %s" % (child_pid, config.SIGNER_SOCKET_PATH)) | ||||||
|  |         return | ||||||
|  |  | ||||||
|  |     setproctitle("certidude spawn signer" % section) | ||||||
|     with open(config.SIGNER_PID_PATH, "w") as fh: |     with open(config.SIGNER_PID_PATH, "w") as fh: | ||||||
|         fh.write("%d\n" % os.getpid()) |         fh.write("%d\n" % os.getpid()) | ||||||
|  |  | ||||||
| #        setproctitle("%s spawn %s" % (sys.argv[0], ca.common_name)) |  | ||||||
|     logging.basicConfig( |     logging.basicConfig( | ||||||
|         filename="/var/log/signer.log", |         filename="/var/log/signer.log", | ||||||
|         level=logging.INFO) |         level=logging.INFO) | ||||||
| @@ -140,8 +291,7 @@ def certidude_spawn(kill, no_interaction): | |||||||
|         config.CERTIFICATE_EXTENDED_KEY_USAGE_FLAGS, |         config.CERTIFICATE_EXTENDED_KEY_USAGE_FLAGS, | ||||||
|         config.REVOCATION_LIST_LIFETIME) |         config.REVOCATION_LIST_LIFETIME) | ||||||
|     asyncore.loop() |     asyncore.loop() | ||||||
|     else: |  | ||||||
|         click.echo("Spawned certidude signer process with PID %d at %s" % (child_pid, config.SIGNER_SOCKET_PATH)) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @click.command("client", help="Setup X.509 certificates for application") | @click.command("client", help="Setup X.509 certificates for application") | ||||||
| @@ -894,6 +1044,9 @@ def certidude_setup_openvpn(): pass | |||||||
| @click.group("setup", help="Getting started section") | @click.group("setup", help="Getting started section") | ||||||
| def certidude_setup(): pass | def certidude_setup(): pass | ||||||
|  |  | ||||||
|  | @click.group("spawn", help="Spawn helper processes") | ||||||
|  | def certidude_spawn(): pass | ||||||
|  |  | ||||||
| @click.group() | @click.group() | ||||||
| def entry_point(): pass | def entry_point(): pass | ||||||
|  |  | ||||||
| @@ -907,6 +1060,8 @@ certidude_setup.add_command(certidude_setup_openvpn) | |||||||
| certidude_setup.add_command(certidude_setup_strongswan) | certidude_setup.add_command(certidude_setup_strongswan) | ||||||
| certidude_setup.add_command(certidude_setup_client) | certidude_setup.add_command(certidude_setup_client) | ||||||
| certidude_setup.add_command(certidude_setup_production) | certidude_setup.add_command(certidude_setup_production) | ||||||
|  | certidude_spawn.add_command(certidude_spawn_request) | ||||||
|  | certidude_spawn.add_command(certidude_spawn_signer) | ||||||
| entry_point.add_command(certidude_setup) | entry_point.add_command(certidude_setup) | ||||||
| entry_point.add_command(certidude_serve) | entry_point.add_command(certidude_serve) | ||||||
| entry_point.add_command(certidude_spawn) | entry_point.add_command(certidude_spawn) | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								certidude/errors.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								certidude/errors.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  |  | ||||||
|  | class RequestExists(Exception): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  | class FatalError(Exception): | ||||||
|  |     """ | ||||||
|  |     Exception to be raised when user intervention is required | ||||||
|  |     """ | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  | class DuplicateCommonNameError(FatalError): | ||||||
|  |     pass | ||||||
| @@ -1,11 +1,13 @@ | |||||||
|  |  | ||||||
| import click | import click | ||||||
| import os | import os | ||||||
|  | import requests | ||||||
| import urllib.request | import urllib.request | ||||||
|  | from certidude import errors | ||||||
| from certidude.wrappers import Certificate, Request | from certidude.wrappers import Certificate, Request | ||||||
| from OpenSSL import crypto | from OpenSSL import crypto | ||||||
|  |  | ||||||
| def certidude_request_certificate(url, key_path, request_path, certificate_path, authority_path, common_name, org_unit, email_address=None, given_name=None, surname=None, autosign=False, wait=False, key_usage=None, extended_key_usage=None, ip_address=None, dns=None): | def certidude_request_certificate(url, key_path, request_path, certificate_path, authority_path, common_name, org_unit=None, email_address=None, given_name=None, surname=None, autosign=False, wait=False, key_usage=None, extended_key_usage=None, ip_address=None, dns=None): | ||||||
|     """ |     """ | ||||||
|     Exchange CSR for certificate using Certidude HTTP API server |     Exchange CSR for certificate using Certidude HTTP API server | ||||||
|     """ |     """ | ||||||
| @@ -18,12 +20,10 @@ def certidude_request_certificate(url, key_path, request_path, certificate_path, | |||||||
|         request_params.add("wait=forever") |         request_params.add("wait=forever") | ||||||
|  |  | ||||||
|     # Expand ca.example.com to http://ca.example.com/api/ |     # Expand ca.example.com to http://ca.example.com/api/ | ||||||
|     if not "/" in url: |     if not url.endswith("/"): | ||||||
|         url += "/api/" |         url += "/api/" | ||||||
|     if "//" not in url: |     if "//" not in url: | ||||||
|         url = "http://" + url |         url = "http://" + url | ||||||
|     if not url.endswith("/"): |  | ||||||
|         url = url + "/" |  | ||||||
|  |  | ||||||
|     authority_url = url + "certificate" |     authority_url = url + "certificate" | ||||||
|     request_url = url + "request" |     request_url = url + "request" | ||||||
| @@ -31,32 +31,26 @@ def certidude_request_certificate(url, key_path, request_path, certificate_path, | |||||||
|     if request_params: |     if request_params: | ||||||
|         request_url = request_url + "?" + "&".join(request_params) |         request_url = request_url + "?" + "&".join(request_params) | ||||||
|  |  | ||||||
|  |     if os.path.exists(certificate_path): | ||||||
|  |         click.echo("Found certificate: %s" % certificate_path) | ||||||
|  |         # TODO: Check certificate validity, download CRL? | ||||||
|  |         return | ||||||
|  |  | ||||||
|     if os.path.exists(authority_path): |     if os.path.exists(authority_path): | ||||||
|         click.echo("Found CA certificate in: %s" % authority_path) |         click.echo("Found CA certificate in: %s" % authority_path) | ||||||
|     else: |     else: | ||||||
|         if authority_url: |  | ||||||
|         click.echo("Attempting to fetch CA certificate from %s" % authority_url) |         click.echo("Attempting to fetch CA certificate from %s" % authority_url) | ||||||
|             try: |  | ||||||
|                 with urllib.request.urlopen(authority_url) as fh: |  | ||||||
|                     buf = fh.read() |  | ||||||
|                     try: |  | ||||||
|                         cert = crypto.load_certificate(crypto.FILETYPE_PEM, buf) |  | ||||||
|                     except crypto.Error: |  | ||||||
|                         raise ValueError("Failed to parse PEM: %s" % buf) |  | ||||||
|                     with open(authority_path + ".part", "wb") as oh: |  | ||||||
|                         oh.write(buf) |  | ||||||
|                     click.echo("Writing CA certificate to: %s" % authority_path) |  | ||||||
|                     os.rename(authority_path + ".part", authority_path) |  | ||||||
|             except urllib.error.HTTPError as e: |  | ||||||
|                 click.echo("Failed to fetch CA certificate, server responded with: %d %s" % (e.code, e.reason), err=True) |  | ||||||
|                 return 1 |  | ||||||
|         else: |  | ||||||
|             raise FileNotFoundError("CA certificate not found and no URL specified") |  | ||||||
|  |  | ||||||
|         try: |         try: | ||||||
|         certificate = Certificate(open(certificate_path)) |             r = requests.get(authority_url) | ||||||
|         click.echo("Found certificate: %s" % certificate_path) |             cert = crypto.load_certificate(crypto.FILETYPE_PEM, r.text) | ||||||
|     except FileNotFoundError: |         except crypto.Error: | ||||||
|  |             raise ValueError("Failed to parse PEM: %s" % r.text) | ||||||
|  |         with open(authority_path + ".part", "w") as oh: | ||||||
|  |             oh.write(r.text) | ||||||
|  |         click.echo("Writing CA certificate to: %s" % authority_path) | ||||||
|  |         os.rename(authority_path + ".part", authority_path) | ||||||
|  |  | ||||||
|     try: |     try: | ||||||
|         request = Request(open(request_path)) |         request = Request(open(request_path)) | ||||||
|         click.echo("Found signing request: %s" % request_path) |         click.echo("Found signing request: %s" % request_path) | ||||||
| @@ -117,42 +111,33 @@ def certidude_request_certificate(url, key_path, request_path, certificate_path, | |||||||
|         os.rename(request_path + ".part", request_path) |         os.rename(request_path + ".part", request_path) | ||||||
|  |  | ||||||
|  |  | ||||||
|         with open(request_path, "rb") as fh: |  | ||||||
|             buf = fh.read() |  | ||||||
|             submission = urllib.request.Request(request_url, buf) |  | ||||||
|             submission.add_header("User-Agent", "Certidude") |  | ||||||
|             submission.add_header("Content-Type", "application/pkcs10") |  | ||||||
|             submission.add_header("Accept", "application/x-x509-user-cert") |  | ||||||
|  |  | ||||||
|     click.echo("Submitting to %s, waiting for response..." % request_url) |     click.echo("Submitting to %s, waiting for response..." % request_url) | ||||||
|             try: |     submission = requests.post(request_url, | ||||||
|                 response = urllib.request.urlopen(submission) |         data=open(request_path), | ||||||
|                 buf = response.read() |         headers={"User-Agent": "Certidude", "Content-Type": "application/pkcs10", "Accept": "application/x-x509-user-cert"}) | ||||||
|                 if response.code == 202: |  | ||||||
|                     click.echo("No waiting was requested and server responded with 202 Accepted, run this command again once the certificate is signed") |     if submission.status_code == requests.codes.ok: | ||||||
|                     return 1 |         pass | ||||||
|                 assert buf, "Server responded with no body, status code %d" % response.code |     if submission.status_code == requests.codes.accepted: | ||||||
|                 cert = crypto.load_certificate(crypto.FILETYPE_PEM, buf) |         # Server stored the request for processing (202 Accepted), but waiting was not requested, hence quitting for now | ||||||
|             except crypto.Error: |         return | ||||||
|                 if buf == b'-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----\n': |     if submission.status_code == requests.codes.conflict: | ||||||
|  |         raise errors.DuplicateCommonNameError("Different signing request with same CN is already present on server, server refuses to overwrite") | ||||||
|  |     else: | ||||||
|  |         submission.raise_for_status() | ||||||
|  |  | ||||||
|  |     if submission.text == '-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----\n': | ||||||
|  |         # Should the client retry or disable request submission? | ||||||
|         raise ValueError("Server refused to sign the request") # TODO: Raise proper exception |         raise ValueError("Server refused to sign the request") # TODO: Raise proper exception | ||||||
|                 else: |  | ||||||
|  |     try: | ||||||
|  |         cert = crypto.load_certificate(crypto.FILETYPE_PEM, submission.text) | ||||||
|  |     except crypto.Error: | ||||||
|         raise ValueError("Failed to parse PEM: %s" % buf) |         raise ValueError("Failed to parse PEM: %s" % buf) | ||||||
|             except urllib.error.HTTPError as e: |  | ||||||
|                 if e.code == 409: |  | ||||||
|                     click.echo("Different signing request with same CN is already present on server, server refuses to overwrite", err=True) |  | ||||||
|                     return 2 |  | ||||||
|                 else: |  | ||||||
|                     click.echo("Failed to fetch certificate, server responded with: %d %s" % (e.code, e.reason), err=True) |  | ||||||
|                     return 3 |  | ||||||
|             else: |  | ||||||
|                 if response.code == 202: |  | ||||||
|                     click.echo("Server stored the request for processing (202 Accepted), but waiting was not requested, hence quitting for now", err=True) |  | ||||||
|                     return 254 |  | ||||||
|  |  | ||||||
|     os.umask(0o022) |     os.umask(0o022) | ||||||
|             with open(certificate_path + ".part", "wb") as gh: |     with open(certificate_path + ".part", "w") as fh: | ||||||
|                 gh.write(buf) |         fh.write(submission.text) | ||||||
|  |  | ||||||
|     click.echo("Writing certificate to: %s" % certificate_path) |     click.echo("Writing certificate to: %s" % certificate_path) | ||||||
|     os.rename(certificate_path + ".part", certificate_path) |     os.rename(certificate_path + ".part", certificate_path) | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ cryptography==1.0 | |||||||
| falcon==0.3.0 | falcon==0.3.0 | ||||||
| humanize==0.5.1 | humanize==0.5.1 | ||||||
| idna==2.0 | idna==2.0 | ||||||
|  | ipsecparse==0.1.0 | ||||||
| Jinja2==2.8 | Jinja2==2.8 | ||||||
| ldap3==0.9.8.8 | ldap3==0.9.8.8 | ||||||
| MarkupSafe==0.23 | MarkupSafe==0.23 | ||||||
| @@ -14,5 +15,6 @@ pycrypto==2.6.1 | |||||||
| pykerberos==1.1.8 | pykerberos==1.1.8 | ||||||
| pyOpenSSL==0.15.1 | pyOpenSSL==0.15.1 | ||||||
| python-mimeparse==0.1.4 | python-mimeparse==0.1.4 | ||||||
|  | requests==2.2.1 | ||||||
| setproctitle==1.1.9 | setproctitle==1.1.9 | ||||||
| six==1.9.0 | six==1.9.0 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user