mirror of
				https://github.com/laurivosandi/certidude
				synced 2025-10-30 17:09:19 +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 |   system_site_packages: true | ||||||
| install: | install: | ||||||
|   - pip install -r requirements.txt |   - pip install -r requirements.txt | ||||||
|  |   - pip install codecov pytest-cov | ||||||
|   - pip install --editable . |   - pip install --editable . | ||||||
|   - pip install codecov pytest-cov click ipaddress humanize falcon simplepam |  | ||||||
| script: | script: | ||||||
|   - sudo useradd adminbot -G sudo -p '$1$PBkf5waA$n9EV6WJ7PS6lyGWkgeTPf1' |   - sudo useradd adminbot -G sudo -p '$1$PBkf5waA$n9EV6WJ7PS6lyGWkgeTPf1' | ||||||
|   - sudo useradd userbot -G users -p '$1$PBkf5waA$n9EV6WJ7PS6lyGWkgeTPf1' |   - sudo useradd userbot -G users -p '$1$PBkf5waA$n9EV6WJ7PS6lyGWkgeTPf1' | ||||||
| @@ -20,12 +20,12 @@ cache: | |||||||
|   directories: |   directories: | ||||||
|     - $HOME/.cache/pip |     - $HOME/.cache/pip | ||||||
| addons: | addons: | ||||||
|  |   hostname: ca | ||||||
|  |   hosts: | ||||||
|  |     - ca.example.lan | ||||||
|  |     - vpn.example.lan | ||||||
|  |     - ipsec.example.lan | ||||||
|   apt: |   apt: | ||||||
|     packages: |     packages: | ||||||
|     - python-xattr |     - python-click | ||||||
|     - python-setproctitle |  | ||||||
|     - python-markdown |  | ||||||
|     - python-jinja2 |  | ||||||
|     - python-configparser |     - python-configparser | ||||||
|     - python-pyasn1 |  | ||||||
|     - python-openssl |  | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ import falcon | |||||||
| import logging | import logging | ||||||
| import xattr | import xattr | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
| from pyasn1.codec.der import decoder |  | ||||||
| from certidude import config, authority, push | from certidude import config, authority, push | ||||||
| from certidude.auth import login_required, authorize_admin | from certidude.auth import login_required, authorize_admin | ||||||
| from certidude.decorators import serialize | 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 configparser import ConfigParser, NoOptionError, NoSectionError | ||||||
| from certidude.helpers import certidude_request_certificate | from certidude.helpers import certidude_request_certificate | ||||||
| from certidude.common import expand_paths, ip_address, ip_network, apt, rpm, pip | 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 datetime import datetime, timedelta | ||||||
| from jinja2 import Environment, PackageLoader |  | ||||||
| import const | import const | ||||||
|  |  | ||||||
| logger = logging.getLogger(__name__) | 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 | # http://www.mad-hacking.net/documentation/linux/security/ssl-tls/creating-ca.xml | ||||||
| # https://kjur.github.io/jsrsasign/ | # 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.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("-r", "--renew", default=False, is_flag=True, help="Renew now") | ||||||
| @click.option("-f", "--fork", default=False, is_flag=True, help="Fork to background") | @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 |     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): |     if not os.path.exists(const.CLIENT_CONFIG_PATH): | ||||||
|         click.echo("No %s!" % 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)) |     service_config.readfp(open(const.SERVICES_CONFIG_PATH)) | ||||||
|  |  | ||||||
|     # Process directories |     # 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 = globals() | ||||||
|     context.update(locals()) |     context.update(locals()) | ||||||
|  |  | ||||||
| @@ -82,7 +77,7 @@ def certidude_request(fork, renew): | |||||||
|         try: |         try: | ||||||
|             endpoint_dhparam = clients.get(authority, "dhparam path") |             endpoint_dhparam = clients.get(authority, "dhparam path") | ||||||
|             if not os.path.exists(endpoint_dhparam): |             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) |                 subprocess.check_call(cmd) | ||||||
|         except NoOptionError: |         except NoOptionError: | ||||||
|             pass |             pass | ||||||
| @@ -125,7 +120,7 @@ def certidude_request(fork, renew): | |||||||
|         elif clients.get(authority, "trigger") != "interface up": |         elif clients.get(authority, "trigger") != "interface up": | ||||||
|             continue |             continue | ||||||
|  |  | ||||||
|         pid_path = os.path.join(run_dir, authority + ".pid") |         pid_path = os.path.join(const.RUN_DIR, authority + ".pid") | ||||||
|  |  | ||||||
|         try: |         try: | ||||||
|             with open(pid_path) as fh: |             with open(pid_path) as fh: | ||||||
| @@ -163,7 +158,7 @@ def certidude_request(fork, renew): | |||||||
|                     endpoint_common_name, |                     endpoint_common_name, | ||||||
|                     insecure=endpoint_insecure, |                     insecure=endpoint_insecure, | ||||||
|                     autosign=True, |                     autosign=True, | ||||||
|                     wait=True, |                     wait=not no_wait, | ||||||
|                     renew=renew) |                     renew=renew) | ||||||
|                 break |                 break | ||||||
|             except requests.exceptions.Timeout: |             except requests.exceptions.Timeout: | ||||||
| @@ -337,6 +332,7 @@ def certidude_request(fork, renew): | |||||||
|  |  | ||||||
| @click.command("server", help="Set up OpenVPN server") | @click.command("server", help="Set up OpenVPN server") | ||||||
| @click.argument("authority") | @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("--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("--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") | @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", |     default="/etc/openvpn/site-to-client.conf", | ||||||
|     type=click.File(mode="w", atomic=True, lazy=True), |     type=click.File(mode="w", atomic=True, lazy=True), | ||||||
|     help="OpenVPN configuration file") |     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 |     # Install dependencies | ||||||
|     apt("openvpn") |     apt("openvpn") | ||||||
|     rpm("openvpn") |     rpm("openvpn") | ||||||
| @@ -358,11 +354,13 @@ def certidude_setup_openvpn_server(authority, config, subnet, route, local, prot | |||||||
|     if client_config.has_section(authority): |     if client_config.has_section(authority): | ||||||
|         click.echo("Section '%s' already exists in %s, remove to regenerate" % (authority, const.CLIENT_CONFIG_PATH)) |         click.echo("Section '%s' already exists in %s, remove to regenerate" % (authority, const.CLIENT_CONFIG_PATH)) | ||||||
|     else: |     else: | ||||||
|  |         client_config.add_section(authority) | ||||||
|         client_config.set(authority, "trigger", "interface up") |         client_config.set(authority, "trigger", "interface up") | ||||||
|         client_config.set(authority, "common name", const.HOSTNAME) |         client_config.set(authority, "common name", common_name) | ||||||
|         client_config.set(authority, "request path", "/etc/openvpn/keys/%s.csr" % const.HOSTNAME) |         slug = common_name.replace(".", "-") | ||||||
|         client_config.set(authority, "key path", "/etc/openvpn/keys/%s.key" % const.HOSTNAME) |         client_config.set(authority, "request path", "/etc/openvpn/keys/%s.csr" % slug) | ||||||
|         client_config.set(authority, "certificate path", "/etc/openvpn/keys/%s.crt" % const.HOSTNAME) |         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, "authority path",  "/etc/openvpn/keys/ca.crt") | ||||||
|         client_config.set(authority, "revocations path",  "/etc/openvpn/keys/ca.crl") |         client_config.set(authority, "revocations path",  "/etc/openvpn/keys/ca.crl") | ||||||
|         client_config.set(authority, "dhparam path", "/etc/openvpn/keys/dhparam.pem") |         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 |     # 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() |     service_config = ConfigParser() | ||||||
|     if os.path.exists(const.SERVICES_CONFIG_PATH): |     if os.path.exists(const.SERVICES_CONFIG_PATH): | ||||||
|         service_config.readfp(open(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.command("client", help="Set up OpenVPN client") | ||||||
| @click.argument("authority") | @click.argument("authority") | ||||||
| @click.argument("remote") | @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('--proto', "-t", default="udp", type=click.Choice(['udp', 'tcp']), help="OpenVPN transport protocol, UDP by default") | ||||||
| @click.option("--config", "-o", | @click.option("--config", "-o", | ||||||
|     default="/etc/openvpn/client-to-site.conf", |     default="/etc/openvpn/client-to-site.conf", | ||||||
|     type=click.File(mode="w", atomic=True, lazy=True), |     type=click.File(mode="w", atomic=True, lazy=True), | ||||||
|     help="OpenVPN configuration file") |     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 |     # Install dependencies | ||||||
|     apt("openvpn") |     apt("openvpn") | ||||||
|     rpm("openvpn") |     rpm("openvpn") | ||||||
| @@ -506,10 +505,10 @@ def certidude_setup_openvpn_client(authority, remote, config, proto): | |||||||
|     else: |     else: | ||||||
|         client_config.add_section(authority) |         client_config.add_section(authority) | ||||||
|         client_config.set(authority, "trigger", "interface up") |         client_config.set(authority, "trigger", "interface up") | ||||||
|         client_config.set(authority, "common name", const.HOSTNAME) |         client_config.set(authority, "common name", common_name) | ||||||
|         client_config.set(authority, "request path", "/etc/openvpn/keys/%s.csr" % const.HOSTNAME) |         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" % const.HOSTNAME) |         client_config.set(authority, "key path", "/etc/openvpn/keys/%s.key" % common_name) | ||||||
|         client_config.set(authority, "certificate path", "/etc/openvpn/keys/%s.crt" % const.HOSTNAME) |         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, "authority path",  "/etc/openvpn/keys/ca.crt") | ||||||
|         client_config.set(authority, "revocations path",  "/etc/openvpn/keys/ca.crl") |         client_config.set(authority, "revocations path",  "/etc/openvpn/keys/ca.crl") | ||||||
|         with open(const.CLIENT_CONFIG_PATH + ".part", 'wb') as fh: |         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("group nogroup\n") | ||||||
|     config.write("persist-tun\n") |     config.write("persist-tun\n") | ||||||
|     config.write("persist-key\n") |     config.write("persist-key\n") | ||||||
|     config.write("up /etc/openvpn/update-resolv-conf") |     config.write("up /etc/openvpn/update-resolv-conf\n") | ||||||
|     config.write("down /etc/openvpn/update-resolv-conf") |     config.write("down /etc/openvpn/update-resolv-conf\n") | ||||||
|  |  | ||||||
|     click.echo("Generated %s" % config.name) |     click.echo("Generated %s" % config.name) | ||||||
|     click.echo("Inspect generated files and issue following to request certificate:") |     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("--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) | @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): | 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 |     if "." not in common_name: | ||||||
|     apt("python-setproctitle python-openssl python-falcon python-humanize python-markdown python-xattr") | 	raise ValueError("No FQDN configured on this system!") | ||||||
|     rpm("python-setproctitle pyOpenSSL python-falcon python-humanize python-markdown pyxattr") |     # Install only rarely changing stuff from OS package management | ||||||
|     pip("gssapi") |     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 |     # Generate secret for tokens | ||||||
|     token_secret = ''.join(random.choice(string.letters + string.digits + '!@#$%^&*()') for i in range(50)) |     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) |         click.echo("Signing %s..." % cert.subject) | ||||||
|  |  | ||||||
|         # Create authority directory with 750 permissions |         # Create directories with 770 permissions | ||||||
|         os.umask(0o027) |         os.umask(0o027) | ||||||
|         if not os.path.exists(directory): |         if not os.path.exists(directory): | ||||||
|             os.makedirs(directory) |             os.makedirs(directory) | ||||||
|  |  | ||||||
|         # Create subdirectories with 770 permissions |         # Create subdirectories with 770 permissions | ||||||
|         os.umask(0o007) |         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)): |             if not os.path.exists(os.path.join(directory, subdir)): | ||||||
|                 os.mkdir(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 |         # Set permission bits to 640 | ||||||
|         os.umask(0o137) |         os.umask(0o137) | ||||||
|         with open(ca_crt, "wb") as fh: |         with open(ca_crt, "wb") as fh: | ||||||
| @@ -1138,6 +1153,11 @@ def certidude_serve(port, listen, fork): | |||||||
|  |  | ||||||
|     # Fetch UID, GID of certidude user |     # Fetch UID, GID of certidude user | ||||||
|     if os.getuid() == 0: |     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 |         import pwd | ||||||
|         _, _, uid, gid, gecos, root, shell = pwd.getpwnam("certidude") |         _, _, uid, gid, gecos, root, shell = pwd.getpwnam("certidude") | ||||||
|         restricted_groups = [] |         restricted_groups = [] | ||||||
| @@ -1211,7 +1231,6 @@ def certidude_serve(port, listen, fork): | |||||||
|     click.echo("Listening on %s:%d" % (listen, port)) |     click.echo("Listening on %s:%d" % (listen, port)) | ||||||
|  |  | ||||||
|     app = certidude_app(log_handlers) |     app = certidude_app(log_handlers) | ||||||
|  |  | ||||||
|     httpd = make_server(listen, port, app, ThreadingWSGIServer) |     httpd = make_server(listen, port, app, ThreadingWSGIServer) | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -1219,29 +1238,18 @@ def certidude_serve(port, listen, fork): | |||||||
|     Drop privileges |     Drop privileges | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|     if os.getuid() == 0: |  | ||||||
|  |  | ||||||
|     # Initialize LDAP service ticket |     # Initialize LDAP service ticket | ||||||
|     if os.path.exists("/etc/cron.hourly/certidude"): |     if os.path.exists("/etc/cron.hourly/certidude"): | ||||||
|         os.system("/etc/cron.hourly/certidude") |         os.system("/etc/cron.hourly/certidude") | ||||||
|  |  | ||||||
|         # Drop privileges |  | ||||||
|         if config.AUTHENTICATION_BACKENDS == {"pam"}: |  | ||||||
|     # PAM needs access to /etc/shadow |     # PAM needs access to /etc/shadow | ||||||
|  |     if config.AUTHENTICATION_BACKENDS == {"pam"}: | ||||||
|         import grp |         import grp | ||||||
|         name, passwd, num, mem = grp.getgrnam("shadow") |         name, passwd, num, mem = grp.getgrnam("shadow") | ||||||
|         click.echo("Adding current user to shadow group due to PAM authentication backend") |         click.echo("Adding current user to shadow group due to PAM authentication backend") | ||||||
|         restricted_groups.append(num) |         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: |     if config.EVENT_SOURCE_PUBLISH: | ||||||
|         from certidude.push import EventSourceLogHandler |         from certidude.push import EventSourceLogHandler | ||||||
|         log_handlers.append(EventSourceLogHandler()) |         log_handlers.append(EventSourceLogHandler()) | ||||||
| @@ -1254,13 +1262,28 @@ def certidude_serve(port, listen, fork): | |||||||
|                     j.addHandler(handler) |                     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(): |         def exit_handler(): | ||||||
|             logger.debug("Shutting down Certidude") |             logger.debug("Shutting down Certidude") | ||||||
|         import atexit |         import atexit | ||||||
|         atexit.register(exit_handler) |         atexit.register(exit_handler) | ||||||
|         logger.debug("Started Certidude at %s", const.FQDN) |         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() |         httpd.serve_forever() | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,13 +1,14 @@ | |||||||
|  |  | ||||||
| import os | import os | ||||||
| import click | import click | ||||||
| import ipaddress |  | ||||||
| import subprocess | import subprocess | ||||||
|  |  | ||||||
| def ip_network(j): | def ip_network(j): | ||||||
|  |     import ipaddress | ||||||
|     return ipaddress.ip_network(unicode(j)) |     return ipaddress.ip_network(unicode(j)) | ||||||
|  |  | ||||||
| def ip_address(j): | def ip_address(j): | ||||||
|  |     import ipaddress | ||||||
|     return ipaddress.ip_address(unicode(j)) |     return ipaddress.ip_address(unicode(j)) | ||||||
|  |  | ||||||
| def expand_paths(): | 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_PUBLISH = cp.get("push", "long poll publish") | ||||||
| LONG_POLL_SUBSCRIBE = cp.get("push", "long poll subscribe") | 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 = "" |     EVENT_SOURCE_PUBLISH = "" | ||||||
|     LONG_POLL_PUBLISH = "" |     LONG_POLL_PUBLISH = "" | ||||||
|     LONG_POLL_SUBSCRIBE = "//nonexistant/lp/sub/%s" |     LONG_POLL_SUBSCRIBE = "//nonexistant/lp/sub/%s" | ||||||
|   | |||||||
| @@ -4,21 +4,18 @@ import os | |||||||
| import socket | import socket | ||||||
| import sys | import sys | ||||||
|  |  | ||||||
|  | RUN_DIR = "/run/certidude" | ||||||
| CONFIG_DIR = os.path.expanduser("~/.certidude") if os.getuid() else "/etc/certidude" | CONFIG_DIR = os.path.expanduser("~/.certidude") if os.getuid() else "/etc/certidude" | ||||||
| CONFIG_PATH = os.path.join(CONFIG_DIR, "server.conf") | CONFIG_PATH = os.path.join(CONFIG_DIR, "server.conf") | ||||||
|  |  | ||||||
| CLIENT_CONFIG_PATH = os.path.join(CONFIG_DIR, "client.conf") | CLIENT_CONFIG_PATH = os.path.join(CONFIG_DIR, "client.conf") | ||||||
| SERVICES_CONFIG_PATH = os.path.join(CONFIG_DIR, "services.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" | 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_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" | 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] |     FQDN = socket.getaddrinfo(socket.gethostname(), 0, socket.AF_INET, 0, 0, socket.AI_CANONNAME)[0][3] | ||||||
| except socket.gaierror: | except socket.gaierror: | ||||||
|   | |||||||
| @@ -6,15 +6,7 @@ import tempfile | |||||||
| from base64 import b64encode | from base64 import b64encode | ||||||
| from datetime import datetime, timedelta | from datetime import datetime, timedelta | ||||||
| from certidude import errors, const | 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 configparser import ConfigParser | ||||||
| from cryptography import x509 |  | ||||||
| from cryptography.hazmat.backends import default_backend |  | ||||||
|  |  | ||||||
| def selinux_fixup(path): | 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 |     Exchange CSR for certificate using Certidude HTTP API server | ||||||
|     """ |     """ | ||||||
|     import requests |     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 |     # Create directories | ||||||
|     for path in key_path, request_path, certificate_path, authority_path, revocations_path: |     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 | # Use SQLite backend | ||||||
| backend = sql | backend = sql | ||||||
| database = sqlite://{{ directory }}/db.sqlite | database = sqlite://{{ directory }}/meta/db.sqlite | ||||||
|  |  | ||||||
| [signature] | [signature] | ||||||
| # Server certificate is granted to certificate with | # Server certificate is granted to certificate with | ||||||
|   | |||||||
| @@ -1,7 +1,2 @@ | |||||||
| click>=6.7 | click>=6.7 | ||||||
| configparser>=3.5.0 | 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 |     # Include here only stuff required to run certidude client | ||||||
|     install_requires=[ |     install_requires=[ | ||||||
|         "click", |         "click", | ||||||
|         "cryptography", |         "configparser" | ||||||
|         "configparser", |  | ||||||
|         "jinja2", |  | ||||||
|         "pyasn1", |  | ||||||
|         "requests", |  | ||||||
|         "requests-kerberos" |  | ||||||
|     ], |     ], | ||||||
|     scripts=[ |     scripts=[ | ||||||
|         "misc/certidude" |         "misc/certidude" | ||||||
|   | |||||||
| @@ -1,18 +1,9 @@ | |||||||
| import os |  | ||||||
| import requests |  | ||||||
| import subprocess | import subprocess | ||||||
| import pwd | import pwd | ||||||
| from falcon import testing |  | ||||||
| from click.testing import CliRunner | from click.testing import CliRunner | ||||||
| from certidude.cli import entry_point as cli | from certidude.cli import entry_point as cli | ||||||
| from datetime import datetime, timedelta | 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 | import pytest | ||||||
| from xattr import setxattr |  | ||||||
|  |  | ||||||
| # pkill py && rm -Rfv ~/.certidude && TRAVIS=1 py.test tests | # pkill py && rm -Rfv ~/.certidude && TRAVIS=1 py.test tests | ||||||
|  |  | ||||||
| @@ -21,9 +12,16 @@ runner = CliRunner() | |||||||
| @pytest.fixture(scope='module') | @pytest.fixture(scope='module') | ||||||
| def client(): | def client(): | ||||||
|     from certidude.api import certidude_app |     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): | 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( |     key = rsa.generate_private_key( | ||||||
|         public_exponent=65537, |         public_exponent=65537, | ||||||
|         key_size=1024, |         key_size=1024, | ||||||
| @@ -36,17 +34,50 @@ def generate_csr(cn=None): | |||||||
|     return buf |     return buf | ||||||
|  |  | ||||||
| def test_cli_setup_authority(): | 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']) |     result = runner.invoke(cli, ['setup', 'authority']) | ||||||
|     assert not result.exception |     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 >= 0x100000000000000000000000000000000000000 | ||||||
|     assert authority.ca_cert.serial_number <= 0xfffffffffffffffffffffffffffffffffffffff |     assert authority.ca_cert.serial_number <= 0xfffffffffffffffffffffffffffffffffffffff | ||||||
|     assert authority.ca_cert.not_valid_before < datetime.now() |     assert authority.ca_cert.not_valid_before < datetime.now() | ||||||
|     assert authority.ca_cert.not_valid_after > datetime.now() + timedelta(days=7000) |     assert authority.ca_cert.not_valid_after > datetime.now() + timedelta(days=7000) | ||||||
|  |  | ||||||
|     # Start server before any signing operations are performed |     # 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 |     assert not result.exception | ||||||
|  |  | ||||||
|     # Password is bot, users created by Travis |     # 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 |     # Insert lease as if VPN gateway had submitted it | ||||||
|     path, _, _ = authority.get_signed("test2") |     path, _, _ = authority.get_signed("test2") | ||||||
|  |     from xattr import setxattr | ||||||
|     setxattr(path, "user.lease.address", b"127.0.0.1") |     setxattr(path, "user.lease.address", b"127.0.0.1") | ||||||
|     setxattr(path, "user.lease.last_seen", b"random") |     setxattr(path, "user.lease.last_seen", b"random") | ||||||
|     r = client().simulate_get("/api/signed/test2/attr/") |     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) |     r2 = client().simulate_get("/api/token/", query_string=r.content) | ||||||
|     assert r2.status_code == 200 # token consumed by anyone on unknown device |     assert r2.status_code == 200 # token consumed by anyone on unknown device | ||||||
|     assert r2.headers.get('content-type') == "application/x-pkcs12" |     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