mirror of
				https://github.com/laurivosandi/certidude
				synced 2025-10-31 01:19:11 +00:00 
			
		
		
		
	Attempt to run client as part of unittests
This commit is contained in:
		
							
								
								
									
										14
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -9,8 +9,8 @@ virtualenv: | ||||
|   system_site_packages: true | ||||
| install: | ||||
|   - pip install -r requirements.txt | ||||
|   - pip install codecov pytest-cov | ||||
|   - pip install --editable . | ||||
|   - pip install codecov pytest-cov click ipaddress humanize falcon simplepam | ||||
| script: | ||||
|   - sudo useradd adminbot -G sudo -p '$1$PBkf5waA$n9EV6WJ7PS6lyGWkgeTPf1' | ||||
|   - sudo useradd userbot -G users -p '$1$PBkf5waA$n9EV6WJ7PS6lyGWkgeTPf1' | ||||
| @@ -20,12 +20,12 @@ cache: | ||||
|   directories: | ||||
|     - $HOME/.cache/pip | ||||
| addons: | ||||
|   hostname: ca | ||||
|   hosts: | ||||
|     - ca.example.lan | ||||
|     - vpn.example.lan | ||||
|     - ipsec.example.lan | ||||
|   apt: | ||||
|     packages: | ||||
|     - python-xattr | ||||
|     - python-setproctitle | ||||
|     - python-markdown | ||||
|     - python-jinja2 | ||||
|     - python-click | ||||
|     - python-configparser | ||||
|     - python-pyasn1 | ||||
|     - python-openssl | ||||
|   | ||||
| @@ -4,7 +4,6 @@ import falcon | ||||
| import logging | ||||
| import xattr | ||||
| from datetime import datetime | ||||
| from pyasn1.codec.der import decoder | ||||
| from certidude import config, authority, push | ||||
| from certidude.auth import login_required, authorize_admin | ||||
| from certidude.decorators import serialize | ||||
|   | ||||
							
								
								
									
										123
									
								
								certidude/cli.py
									
									
									
									
									
								
							
							
						
						
									
										123
									
								
								certidude/cli.py
									
									
									
									
									
								
							| @@ -16,18 +16,10 @@ import sys | ||||
| from configparser import ConfigParser, NoOptionError, NoSectionError | ||||
| from certidude.helpers import certidude_request_certificate | ||||
| from certidude.common import expand_paths, ip_address, ip_network, apt, rpm, pip | ||||
| from cryptography import x509 | ||||
| from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID | ||||
| from cryptography.hazmat.backends import default_backend | ||||
| from cryptography.hazmat.primitives import serialization | ||||
| from cryptography.hazmat.primitives import hashes, serialization | ||||
| from cryptography.hazmat.primitives.asymmetric import rsa | ||||
| from datetime import datetime, timedelta | ||||
| from jinja2 import Environment, PackageLoader | ||||
| import const | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
| env = Environment(loader=PackageLoader("certidude", "templates"), trim_blocks=True) | ||||
|  | ||||
| # http://www.mad-hacking.net/documentation/linux/security/ssl-tls/creating-ca.xml | ||||
| # https://kjur.github.io/jsrsasign/ | ||||
| @@ -41,8 +33,13 @@ NOW = datetime.utcnow().replace(tzinfo=None) | ||||
| @click.command("request", help="Run processes for requesting certificates and configuring services") | ||||
| @click.option("-r", "--renew", default=False, is_flag=True, help="Renew now") | ||||
| @click.option("-f", "--fork", default=False, is_flag=True, help="Fork to background") | ||||
| def certidude_request(fork, renew): | ||||
| @click.option("-nw", "--no-wait", default=False, is_flag=True, help="Return immideately if server doesn't autosign") | ||||
| def certidude_request(fork, renew, no_wait): | ||||
|     rpm("openssl") | ||||
|     apt("openssl") | ||||
|     import requests | ||||
|     from jinja2 import Environment, PackageLoader | ||||
|     env = Environment(loader=PackageLoader("certidude", "templates"), trim_blocks=True) | ||||
|  | ||||
|     if not os.path.exists(const.CLIENT_CONFIG_PATH): | ||||
|         click.echo("No %s!" % const.CLIENT_CONFIG_PATH) | ||||
| @@ -59,12 +56,10 @@ def certidude_request(fork, renew): | ||||
|     service_config.readfp(open(const.SERVICES_CONFIG_PATH)) | ||||
|  | ||||
|     # Process directories | ||||
|     run_dir = "/run/certidude" | ||||
|     if not os.path.exists(const.RUN_DIR): | ||||
|         click.echo("Creating: %s" % const.RUN_DIR) | ||||
|         os.makedirs(const.RUN_DIR) | ||||
|  | ||||
|     # Prepare signer PID-s directory | ||||
|     if not os.path.exists(run_dir): | ||||
|         click.echo("Creating: %s" % run_dir) | ||||
|         os.makedirs(run_dir) | ||||
|     context = globals() | ||||
|     context.update(locals()) | ||||
|  | ||||
| @@ -82,7 +77,7 @@ def certidude_request(fork, renew): | ||||
|         try: | ||||
|             endpoint_dhparam = clients.get(authority, "dhparam path") | ||||
|             if not os.path.exists(endpoint_dhparam): | ||||
|                 cmd = "openssl", "dhparam", "-out", endpoint_dhparam, "2048" | ||||
|                 cmd = "openssl", "dhparam", "-out", endpoint_dhparam, ("512" if os.getenv("TRAVIS") else "2048") | ||||
|                 subprocess.check_call(cmd) | ||||
|         except NoOptionError: | ||||
|             pass | ||||
| @@ -125,7 +120,7 @@ def certidude_request(fork, renew): | ||||
|         elif clients.get(authority, "trigger") != "interface up": | ||||
|             continue | ||||
|  | ||||
|         pid_path = os.path.join(run_dir, authority + ".pid") | ||||
|         pid_path = os.path.join(const.RUN_DIR, authority + ".pid") | ||||
|  | ||||
|         try: | ||||
|             with open(pid_path) as fh: | ||||
| @@ -163,7 +158,7 @@ def certidude_request(fork, renew): | ||||
|                     endpoint_common_name, | ||||
|                     insecure=endpoint_insecure, | ||||
|                     autosign=True, | ||||
|                     wait=True, | ||||
|                     wait=not no_wait, | ||||
|                     renew=renew) | ||||
|                 break | ||||
|             except requests.exceptions.Timeout: | ||||
| @@ -337,6 +332,7 @@ def certidude_request(fork, renew): | ||||
|  | ||||
| @click.command("server", help="Set up OpenVPN server") | ||||
| @click.argument("authority") | ||||
| @click.option("--common-name", "-cn", default=const.FQDN, help="Common name, %s by default" % const.FQDN) | ||||
| @click.option("--subnet", "-s", default="192.168.33.0/24", type=ip_network, help="OpenVPN subnet, 192.168.33.0/24 by default") | ||||
| @click.option("--local", "-l", default="0.0.0.0", help="OpenVPN listening address, defaults to all interfaces") | ||||
| @click.option("--port", "-p", default=1194, type=click.IntRange(1,60000), help="OpenVPN listening port, 1194 by default") | ||||
| @@ -346,7 +342,7 @@ def certidude_request(fork, renew): | ||||
|     default="/etc/openvpn/site-to-client.conf", | ||||
|     type=click.File(mode="w", atomic=True, lazy=True), | ||||
|     help="OpenVPN configuration file") | ||||
| def certidude_setup_openvpn_server(authority, config, subnet, route, local, proto, port): | ||||
| def certidude_setup_openvpn_server(authority, common_name, config, subnet, route, local, proto, port): | ||||
|     # Install dependencies | ||||
|     apt("openvpn") | ||||
|     rpm("openvpn") | ||||
| @@ -358,11 +354,13 @@ def certidude_setup_openvpn_server(authority, config, subnet, route, local, prot | ||||
|     if client_config.has_section(authority): | ||||
|         click.echo("Section '%s' already exists in %s, remove to regenerate" % (authority, const.CLIENT_CONFIG_PATH)) | ||||
|     else: | ||||
|         client_config.add_section(authority) | ||||
|         client_config.set(authority, "trigger", "interface up") | ||||
|         client_config.set(authority, "common name", const.HOSTNAME) | ||||
|         client_config.set(authority, "request path", "/etc/openvpn/keys/%s.csr" % const.HOSTNAME) | ||||
|         client_config.set(authority, "key path", "/etc/openvpn/keys/%s.key" % const.HOSTNAME) | ||||
|         client_config.set(authority, "certificate path", "/etc/openvpn/keys/%s.crt" % const.HOSTNAME) | ||||
|         client_config.set(authority, "common name", common_name) | ||||
|         slug = common_name.replace(".", "-") | ||||
|         client_config.set(authority, "request path", "/etc/openvpn/keys/%s.csr" % slug) | ||||
|         client_config.set(authority, "key path", "/etc/openvpn/keys/%s.key" % slug) | ||||
|         client_config.set(authority, "certificate path", "/etc/openvpn/keys/%s.crt" % slug) | ||||
|         client_config.set(authority, "authority path",  "/etc/openvpn/keys/ca.crt") | ||||
|         client_config.set(authority, "revocations path",  "/etc/openvpn/keys/ca.crl") | ||||
|         client_config.set(authority, "dhparam path", "/etc/openvpn/keys/dhparam.pem") | ||||
| @@ -373,7 +371,7 @@ def certidude_setup_openvpn_server(authority, config, subnet, route, local, prot | ||||
|  | ||||
|  | ||||
|     # Create corresponding section in /etc/certidude/services.conf | ||||
|     endpoint = "OpenVPN server %s of %s" % (const.FQDN, authority) | ||||
|     endpoint = "OpenVPN server %s of %s" % (common_name, authority) | ||||
|     service_config = ConfigParser() | ||||
|     if os.path.exists(const.SERVICES_CONFIG_PATH): | ||||
|         service_config.readfp(open(const.SERVICES_CONFIG_PATH)) | ||||
| @@ -487,12 +485,13 @@ def certidude_setup_nginx(authority, site_config, tls_config, common_name, direc | ||||
| @click.command("client", help="Set up OpenVPN client") | ||||
| @click.argument("authority") | ||||
| @click.argument("remote") | ||||
| @click.option("--common-name", "-cn", default=const.HOSTNAME, help="Common name, %s by default" % const.HOSTNAME) | ||||
| @click.option('--proto', "-t", default="udp", type=click.Choice(['udp', 'tcp']), help="OpenVPN transport protocol, UDP by default") | ||||
| @click.option("--config", "-o", | ||||
|     default="/etc/openvpn/client-to-site.conf", | ||||
|     type=click.File(mode="w", atomic=True, lazy=True), | ||||
|     help="OpenVPN configuration file") | ||||
| def certidude_setup_openvpn_client(authority, remote, config, proto): | ||||
| def certidude_setup_openvpn_client(authority, remote, common_name, config, proto): | ||||
|     # Install dependencies | ||||
|     apt("openvpn") | ||||
|     rpm("openvpn") | ||||
| @@ -506,10 +505,10 @@ def certidude_setup_openvpn_client(authority, remote, config, proto): | ||||
|     else: | ||||
|         client_config.add_section(authority) | ||||
|         client_config.set(authority, "trigger", "interface up") | ||||
|         client_config.set(authority, "common name", const.HOSTNAME) | ||||
|         client_config.set(authority, "request path", "/etc/openvpn/keys/%s.csr" % const.HOSTNAME) | ||||
|         client_config.set(authority, "key path", "/etc/openvpn/keys/%s.key" % const.HOSTNAME) | ||||
|         client_config.set(authority, "certificate path", "/etc/openvpn/keys/%s.crt" % const.HOSTNAME) | ||||
|         client_config.set(authority, "common name", common_name) | ||||
|         client_config.set(authority, "request path", "/etc/openvpn/keys/%s.csr" % const.common_name) | ||||
|         client_config.set(authority, "key path", "/etc/openvpn/keys/%s.key" % common_name) | ||||
|         client_config.set(authority, "certificate path", "/etc/openvpn/keys/%s.crt" % common_name) | ||||
|         client_config.set(authority, "authority path",  "/etc/openvpn/keys/ca.crt") | ||||
|         client_config.set(authority, "revocations path",  "/etc/openvpn/keys/ca.crl") | ||||
|         with open(const.CLIENT_CONFIG_PATH + ".part", 'wb') as fh: | ||||
| @@ -549,8 +548,8 @@ def certidude_setup_openvpn_client(authority, remote, config, proto): | ||||
|     config.write("group nogroup\n") | ||||
|     config.write("persist-tun\n") | ||||
|     config.write("persist-key\n") | ||||
|     config.write("up /etc/openvpn/update-resolv-conf") | ||||
|     config.write("down /etc/openvpn/update-resolv-conf") | ||||
|     config.write("up /etc/openvpn/update-resolv-conf\n") | ||||
|     config.write("down /etc/openvpn/update-resolv-conf\n") | ||||
|  | ||||
|     click.echo("Generated %s" % config.name) | ||||
|     click.echo("Inspect generated files and issue following to request certificate:") | ||||
| @@ -791,10 +790,21 @@ def certidude_setup_openvpn_networkmanager(authority, remote): | ||||
| @click.option("--server-flags", is_flag=True, help="Add TLS Server and IKE Intermediate extended key usage flags") | ||||
| @click.option("--outbox", default="smtp://smtp.%s" % const.DOMAIN, help="SMTP server, smtp://smtp.%s by default" % const.DOMAIN) | ||||
| def certidude_setup_authority(username, kerberos_keytab, nginx_config, country, state, locality, organization, organizational_unit, common_name, directory, authority_lifetime, push_server, outbox, server_flags): | ||||
|     # Install dependencies | ||||
|     apt("python-setproctitle python-openssl python-falcon python-humanize python-markdown python-xattr") | ||||
|     rpm("python-setproctitle pyOpenSSL python-falcon python-humanize python-markdown pyxattr") | ||||
|     pip("gssapi") | ||||
|     if "." not in common_name: | ||||
| 	raise ValueError("No FQDN configured on this system!") | ||||
|     # Install only rarely changing stuff from OS package management | ||||
|     apt("python-setproctitle cython python-dev libkrb5-dev libldap2-dev libffi-dev libssl-dev") | ||||
|     apt("python-mimeparse python-markdown python-xattr python-jinja2 python-cffi python-openssl") | ||||
|     pip("gssapi falcon cryptography humanize ipaddress simplepam humanize requests") | ||||
|  | ||||
|     from cryptography import x509 | ||||
|     from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID | ||||
|     from cryptography.hazmat.backends import default_backend | ||||
|     from cryptography.hazmat.primitives import serialization | ||||
|     from cryptography.hazmat.primitives import hashes, serialization | ||||
|     from cryptography.hazmat.primitives.asymmetric import rsa | ||||
|     from jinja2 import Environment, PackageLoader | ||||
|     env = Environment(loader=PackageLoader("certidude", "templates"), trim_blocks=True) | ||||
|  | ||||
|     # Generate secret for tokens | ||||
|     token_secret = ''.join(random.choice(string.letters + string.digits + '!@#$%^&*()') for i in range(50)) | ||||
| @@ -961,17 +971,22 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country, | ||||
|  | ||||
|         click.echo("Signing %s..." % cert.subject) | ||||
|  | ||||
|         # Create authority directory with 750 permissions | ||||
|         # Create directories with 770 permissions | ||||
|         os.umask(0o027) | ||||
|         if not os.path.exists(directory): | ||||
|             os.makedirs(directory) | ||||
|  | ||||
|         # Create subdirectories with 770 permissions | ||||
|         os.umask(0o007) | ||||
|         for subdir in ("signed", "requests", "revoked", "expired"): | ||||
|         for subdir in ("signed", "requests", "revoked", "expired", "meta"): | ||||
|             if not os.path.exists(os.path.join(directory, subdir)): | ||||
|                 os.mkdir(os.path.join(directory, subdir)) | ||||
|  | ||||
|         # Create SQLite database file with correct permissions | ||||
|         os.umask(0o117) | ||||
|         with open(os.path.join(directory, "meta", "db.sqlite"), "wb") as fh: | ||||
|             pass | ||||
|  | ||||
|         # Set permission bits to 640 | ||||
|         os.umask(0o137) | ||||
|         with open(ca_crt, "wb") as fh: | ||||
| @@ -1138,6 +1153,11 @@ def certidude_serve(port, listen, fork): | ||||
|  | ||||
|     # Fetch UID, GID of certidude user | ||||
|     if os.getuid() == 0: | ||||
|         # Process directories | ||||
|         if not os.path.exists(const.RUN_DIR): | ||||
|             click.echo("Creating: %s" % const.RUN_DIR) | ||||
|             os.makedirs(const.RUN_DIR) | ||||
|  | ||||
|         import pwd | ||||
|         _, _, uid, gid, gecos, root, shell = pwd.getpwnam("certidude") | ||||
|         restricted_groups = [] | ||||
| @@ -1211,7 +1231,6 @@ def certidude_serve(port, listen, fork): | ||||
|     click.echo("Listening on %s:%d" % (listen, port)) | ||||
|  | ||||
|     app = certidude_app(log_handlers) | ||||
|  | ||||
|     httpd = make_server(listen, port, app, ThreadingWSGIServer) | ||||
|  | ||||
|  | ||||
| @@ -1219,29 +1238,18 @@ def certidude_serve(port, listen, fork): | ||||
|     Drop privileges | ||||
|     """ | ||||
|  | ||||
|     if os.getuid() == 0: | ||||
|  | ||||
|     # Initialize LDAP service ticket | ||||
|     if os.path.exists("/etc/cron.hourly/certidude"): | ||||
|         os.system("/etc/cron.hourly/certidude") | ||||
|  | ||||
|         # Drop privileges | ||||
|         if config.AUTHENTICATION_BACKENDS == {"pam"}: | ||||
|     # PAM needs access to /etc/shadow | ||||
|     if config.AUTHENTICATION_BACKENDS == {"pam"}: | ||||
|         import grp | ||||
|         name, passwd, num, mem = grp.getgrnam("shadow") | ||||
|         click.echo("Adding current user to shadow group due to PAM authentication backend") | ||||
|         restricted_groups.append(num) | ||||
|  | ||||
|         os.setgroups(restricted_groups) | ||||
|         os.setgid(gid) | ||||
|         os.setuid(uid) | ||||
|  | ||||
|         click.echo("Switched to user %s (uid=%d, gid=%d); member of groups %s" % | ||||
|             ("certidude", os.getuid(), os.getgid(), ", ".join([str(j) for j in os.getgroups()]))) | ||||
|  | ||||
|         os.umask(0o007) | ||||
|  | ||||
|     if config.EVENT_SOURCE_PUBLISH: | ||||
|         from certidude.push import EventSourceLogHandler | ||||
|         log_handlers.append(EventSourceLogHandler()) | ||||
| @@ -1254,13 +1262,28 @@ def certidude_serve(port, listen, fork): | ||||
|                     j.addHandler(handler) | ||||
|  | ||||
|  | ||||
|     if not fork or not os.fork(): | ||||
|         pid = os.getpid() | ||||
|         with open(const.SERVER_PID_PATH, "w") as pidfile: | ||||
|             pidfile.write("%d\n" % pid) | ||||
|  | ||||
|         def exit_handler(): | ||||
|             logger.debug("Shutting down Certidude") | ||||
|         import atexit | ||||
|         atexit.register(exit_handler) | ||||
|         logger.debug("Started Certidude at %s", const.FQDN) | ||||
|  | ||||
|     if not fork or not os.fork(): | ||||
|  | ||||
|         # Drop privileges | ||||
|         os.setgroups(restricted_groups) | ||||
|         os.setgid(gid) | ||||
|         os.setuid(uid) | ||||
|  | ||||
|         click.echo("Switched to user %s (uid=%d, gid=%d); member of groups %s" % | ||||
|             ("certidude", os.getuid(), os.getgid(), ", ".join([str(j) for j in os.getgroups()]))) | ||||
|  | ||||
|         os.umask(0o007) | ||||
|  | ||||
|         httpd.serve_forever() | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,13 +1,14 @@ | ||||
|  | ||||
| import os | ||||
| import click | ||||
| import ipaddress | ||||
| import subprocess | ||||
|  | ||||
| def ip_network(j): | ||||
|     import ipaddress | ||||
|     return ipaddress.ip_network(unicode(j)) | ||||
|  | ||||
| def ip_address(j): | ||||
|     import ipaddress | ||||
|     return ipaddress.ip_address(unicode(j)) | ||||
|  | ||||
| def expand_paths(): | ||||
|   | ||||
| @@ -71,7 +71,7 @@ EVENT_SOURCE_SUBSCRIBE = cp.get("push", "event source subscribe") | ||||
| LONG_POLL_PUBLISH = cp.get("push", "long poll publish") | ||||
| LONG_POLL_SUBSCRIBE = cp.get("push", "long poll subscribe") | ||||
|  | ||||
| if os.getenv("TRAVIS"): # TODO: include nginx setup in Travis | ||||
| if const.DOMAIN == "example.lan": # TODO: include nginx setup in Travis | ||||
|     EVENT_SOURCE_PUBLISH = "" | ||||
|     LONG_POLL_PUBLISH = "" | ||||
|     LONG_POLL_SUBSCRIBE = "//nonexistant/lp/sub/%s" | ||||
|   | ||||
| @@ -4,24 +4,21 @@ import os | ||||
| import socket | ||||
| import sys | ||||
|  | ||||
| RUN_DIR = "/run/certidude" | ||||
| CONFIG_DIR = os.path.expanduser("~/.certidude") if os.getuid() else "/etc/certidude" | ||||
| CONFIG_PATH = os.path.join(CONFIG_DIR, "server.conf") | ||||
|  | ||||
| CLIENT_CONFIG_PATH = os.path.join(CONFIG_DIR, "client.conf") | ||||
| SERVICES_CONFIG_PATH = os.path.join(CONFIG_DIR, "services.conf") | ||||
| SERVER_PID_PATH = os.path.join(CONFIG_DIR if os.getuid() else RUN_DIR, "server.pid") | ||||
| SERVER_LOG_PATH = os.path.join(CONFIG_DIR, "server.log") if os.getuid() else "/var/log/certidude-server.log" | ||||
| SIGNER_SOCKET_PATH = os.path.join(CONFIG_DIR, "signer.sock") if os.getuid() else "/run/certidude/signer.sock" | ||||
| SIGNER_PID_PATH = os.path.join(CONFIG_DIR, "signer.pid") if os.getuid() else "/run/certidude/signer.pid" | ||||
| SIGNER_PID_PATH = os.path.join(CONFIG_DIR if os.getuid() else RUN_DIR, "signer.pid") | ||||
| SIGNER_LOG_PATH = os.path.join(CONFIG_DIR, "signer.log") if os.getuid() else "/var/log/certidude-signer.log" | ||||
|  | ||||
| # Work around the 'asn1 encoding routines:ASN1_mbstring_ncopy:string too long' | ||||
| # issue within OpenSSL ASN1 parser while running on Travis | ||||
| if os.getenv("TRAVIS"): | ||||
|     FQDN = "buildbot" | ||||
| else: | ||||
|     try: | ||||
| try: | ||||
|     FQDN = socket.getaddrinfo(socket.gethostname(), 0, socket.AF_INET, 0, 0, socket.AI_CANONNAME)[0][3] | ||||
|     except socket.gaierror: | ||||
| except socket.gaierror: | ||||
|     click.echo("Failed to resolve fully qualified hostname of this machine, make sure hostname -f works") | ||||
|     sys.exit(254) | ||||
|  | ||||
|   | ||||
| @@ -6,15 +6,7 @@ import tempfile | ||||
| from base64 import b64encode | ||||
| from datetime import datetime, timedelta | ||||
| from certidude import errors, const | ||||
| from cryptography import x509 | ||||
| from cryptography.hazmat.primitives.asymmetric import rsa, padding | ||||
| from cryptography.hazmat.backends import default_backend | ||||
| from cryptography.hazmat.primitives import hashes, serialization | ||||
| from cryptography.hazmat.primitives.serialization import Encoding | ||||
| from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID, AuthorityInformationAccessOID | ||||
| from configparser import ConfigParser | ||||
| from cryptography import x509 | ||||
| from cryptography.hazmat.backends import default_backend | ||||
|  | ||||
| def selinux_fixup(path): | ||||
|     """ | ||||
| @@ -30,6 +22,12 @@ def certidude_request_certificate(server, system_keytab_required, key_path, requ | ||||
|     Exchange CSR for certificate using Certidude HTTP API server | ||||
|     """ | ||||
|     import requests | ||||
|     from cryptography import x509 | ||||
|     from cryptography.hazmat.primitives.asymmetric import rsa, padding | ||||
|     from cryptography.hazmat.backends import default_backend | ||||
|     from cryptography.hazmat.primitives import hashes, serialization | ||||
|     from cryptography.hazmat.primitives.serialization import Encoding | ||||
|     from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID, AuthorityInformationAccessOID | ||||
|  | ||||
|     # Create directories | ||||
|     for path in key_path, request_path, certificate_path, authority_path, revocations_path: | ||||
|   | ||||
| @@ -58,7 +58,7 @@ autosign subnets = 127.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 | ||||
|  | ||||
| # Use SQLite backend | ||||
| backend = sql | ||||
| database = sqlite://{{ directory }}/db.sqlite | ||||
| database = sqlite://{{ directory }}/meta/db.sqlite | ||||
|  | ||||
| [signature] | ||||
| # Server certificate is granted to certificate with | ||||
|   | ||||
| @@ -1,7 +1,2 @@ | ||||
| click>=6.7 | ||||
| configparser>=3.5.0 | ||||
| cryptography>=1.7.1 | ||||
| Jinja2>=2.8 | ||||
| pyasn1>=0.1.9 | ||||
| requests>=2.12.4 | ||||
| requests-kerberos>=0.7.0 | ||||
|   | ||||
							
								
								
									
										7
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								setup.py
									
									
									
									
									
								
							| @@ -20,12 +20,7 @@ setup( | ||||
|     # Include here only stuff required to run certidude client | ||||
|     install_requires=[ | ||||
|         "click", | ||||
|         "cryptography", | ||||
|         "configparser", | ||||
|         "jinja2", | ||||
|         "pyasn1", | ||||
|         "requests", | ||||
|         "requests-kerberos" | ||||
|         "configparser" | ||||
|     ], | ||||
|     scripts=[ | ||||
|         "misc/certidude" | ||||
|   | ||||
| @@ -1,18 +1,9 @@ | ||||
| import os | ||||
| import requests | ||||
| import subprocess | ||||
| import pwd | ||||
| from falcon import testing | ||||
| from click.testing import CliRunner | ||||
| from certidude.cli import entry_point as cli | ||||
| from datetime import datetime, timedelta | ||||
| from cryptography import x509 | ||||
| from cryptography.hazmat.primitives.asymmetric import rsa, padding | ||||
| from cryptography.hazmat.primitives import hashes, serialization | ||||
| from cryptography.hazmat.backends import default_backend | ||||
| from cryptography.x509.oid import NameOID | ||||
| import pytest | ||||
| from xattr import setxattr | ||||
|  | ||||
| # pkill py && rm -Rfv ~/.certidude && TRAVIS=1 py.test tests | ||||
|  | ||||
| @@ -21,9 +12,16 @@ runner = CliRunner() | ||||
| @pytest.fixture(scope='module') | ||||
| def client(): | ||||
|     from certidude.api import certidude_app | ||||
|     return testing.TestClient(certidude_app()) | ||||
|     from falcon import testing | ||||
|     app = certidude_app() | ||||
|     return testing.TestClient(app) | ||||
|  | ||||
| def generate_csr(cn=None): | ||||
|     from cryptography import x509 | ||||
|     from cryptography.hazmat.primitives.asymmetric import rsa, padding | ||||
|     from cryptography.hazmat.primitives import hashes, serialization | ||||
|     from cryptography.hazmat.backends import default_backend | ||||
|     from cryptography.x509.oid import NameOID | ||||
|     key = rsa.generate_private_key( | ||||
|         public_exponent=65537, | ||||
|         key_size=1024, | ||||
| @@ -36,17 +34,50 @@ def generate_csr(cn=None): | ||||
|     return buf | ||||
|  | ||||
| def test_cli_setup_authority(): | ||||
|     import shutil | ||||
|     import os | ||||
|     if os.path.exists("/run/certidude/signer.pid"): | ||||
|         with open("/run/certidude/signer.pid") as fh: | ||||
|             try: | ||||
|                 os.kill(int(fh.read()), 15) | ||||
|             except OSError: | ||||
|                 pass | ||||
|         os.unlink("/run/certidude/signer.pid") | ||||
|     if os.path.exists("/run/certidude/server.pid"): | ||||
|         with open("/run/certidude/server.pid") as fh: | ||||
|             try: | ||||
|                 os.kill(int(fh.read()), 15) | ||||
|             except OSError: | ||||
|                 pass | ||||
|         os.unlink("/run/certidude/server.pid") | ||||
|  | ||||
|     if os.path.exists("/var/lib/certidude/ca.example.lan"): | ||||
|         shutil.rmtree("/var/lib/certidude/ca.example.lan") | ||||
|     if os.path.exists("/etc/certidude/server.conf"): | ||||
|         os.unlink("/etc/certidude/server.conf") | ||||
|     if os.path.exists("/etc/certidude/client.conf"): | ||||
|         os.unlink("/etc/certidude/client.conf") | ||||
|  | ||||
|     # Remove OpenVPN stuff | ||||
|     for filename in os.listdir("/etc/openvpn"): | ||||
|         if filename.endswith(".conf"): | ||||
|             os.unlink(os.path.join("/etc/openvpn", filename)) | ||||
|     if os.path.exists("/etc/openvpn/keys"): | ||||
|         shutil.rmtree("/etc/openvpn/keys") | ||||
|  | ||||
|     from certidude import const | ||||
|  | ||||
|     result = runner.invoke(cli, ['setup', 'authority']) | ||||
|     assert not result.exception | ||||
|  | ||||
|     from certidude import const, config, authority | ||||
|     from certidude import config, authority | ||||
|     assert authority.ca_cert.serial_number >= 0x100000000000000000000000000000000000000 | ||||
|     assert authority.ca_cert.serial_number <= 0xfffffffffffffffffffffffffffffffffffffff | ||||
|     assert authority.ca_cert.not_valid_before < datetime.now() | ||||
|     assert authority.ca_cert.not_valid_after > datetime.now() + timedelta(days=7000) | ||||
|  | ||||
|     # Start server before any signing operations are performed | ||||
|     result = runner.invoke(cli, ['serve', '-f', '-p', '8080']) | ||||
|     result = runner.invoke(cli, ['serve', '-f']) | ||||
|     assert not result.exception | ||||
|  | ||||
|     # Password is bot, users created by Travis | ||||
| @@ -186,6 +217,7 @@ def test_cli_setup_authority(): | ||||
|  | ||||
|     # Insert lease as if VPN gateway had submitted it | ||||
|     path, _, _ = authority.get_signed("test2") | ||||
|     from xattr import setxattr | ||||
|     setxattr(path, "user.lease.address", b"127.0.0.1") | ||||
|     setxattr(path, "user.lease.last_seen", b"random") | ||||
|     r = client().simulate_get("/api/signed/test2/attr/") | ||||
| @@ -303,3 +335,25 @@ def test_cli_setup_authority(): | ||||
|     r2 = client().simulate_get("/api/token/", query_string=r.content) | ||||
|     assert r2.status_code == 200 # token consumed by anyone on unknown device | ||||
|     assert r2.headers.get('content-type') == "application/x-pkcs12" | ||||
|  | ||||
|  | ||||
|     result = runner.invoke(cli, ['setup', 'openvpn', 'server', "-cn", "vpn.example.lan", "ca.example.lan"]) | ||||
|     assert not result.exception | ||||
|  | ||||
|     result = runner.invoke(cli, ['setup', 'openvpn', 'client', "-cn", "roadwarrior1", "ca.example.lan", "vpn.example.lan"]) | ||||
|     assert not result.exception | ||||
|  | ||||
|     import os | ||||
|     if not os.path.exists("/etc/openvpn/keys"): | ||||
|         os.makedirs("/etc/openvpn/keys") | ||||
|  | ||||
|     with open("/etc/certidude/client.conf", "a") as fh: | ||||
|         fh.write("insecure = true\n") | ||||
|  | ||||
|     # pregen dhparam | ||||
|     result = runner.invoke(cli, ["request", "--no-wait"]) | ||||
|     assert not result.exception | ||||
|     result = runner.invoke(cli, ['sign', 'vpn.example.lan']) | ||||
|     assert not result.exception | ||||
|     result = runner.invoke(cli, ["request", "--no-wait"]) | ||||
|     assert not result.exception | ||||
|   | ||||
		Reference in New Issue
	
	Block a user