From 505fa9d557dc9dda403399dbfcba40c378c7f342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauri=20V=C3=B5sandi?= Date: Thu, 4 May 2017 06:40:47 +0000 Subject: [PATCH] tests: Fix NetworkManager setup tests --- .travis.yml | 1 - certidude/cli.py | 54 ++++++++++++++++++----------------------------- tests/test_cli.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 74 insertions(+), 35 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9aed2d3..69e0b19 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,6 @@ script: - sudo find /home/ -type d -exec chmod 755 {} \; # Allow certidude serve to read templates - sudo useradd adminbot -G sudo -p '$1$PBkf5waA$n9EV6WJ7PS6lyGWkgeTPf1' - sudo useradd userbot -G users -p '$1$PBkf5waA$n9EV6WJ7PS6lyGWkgeTPf1' - - sudo adduser --system --no-create-home --group certidude - sudo chmod 777 . # Allow forked processes to write .coverage files - sudo coverage run --parallel-mode --source certidude -m py.test tests - sudo coverage combine diff --git a/certidude/cli.py b/certidude/cli.py index 01324ea..b9d6f4f 100755 --- a/certidude/cli.py +++ b/certidude/cli.py @@ -31,6 +31,16 @@ logger = logging.getLogger(__name__) NOW = datetime.utcnow().replace(tzinfo=None) +def fqdn_required(func): + def wrapped(**args): + common_name = args.get("common_name") + if "." in common_name: + logger.info("Using fully qualified hostname %s" % common_name) + else: + raise ValueError("Fully qualified hostname not specified as common name, make sure hostname -f works") + return func(**args) + return wrapped + def setup_client(prefix="client_", dh=False): # Create section in /etc/certidude/client.conf def wrapper(func): @@ -306,7 +316,7 @@ def certidude_request(fork, renew, no_wait): nm_config.set("vpn", "tap-dev", "no") nm_config.set("vpn", "remote-cert-tls", "server") # Assert TLS Server flag of X.509 certificate nm_config.set("vpn", "remote", service_config.get(endpoint, "remote")) - nm_config.set("vpn", "port", endpoint_port) + nm_config.set("vpn", "port", str(endpoint_port)) nm_config.set("vpn", "proto", endpoint_proto) nm_config.set("vpn", "key", endpoint_key_path) nm_config.set("vpn", "cert", endpoint_certificate_path) @@ -383,6 +393,7 @@ def certidude_request(fork, renew, no_wait): default="/etc/openvpn/site-to-client.conf", type=click.File(mode="w", atomic=True, lazy=True), help="OpenVPN configuration file") +@fqdn_required @setup_client(prefix="server_", dh=True) def certidude_setup_openvpn_server(authority, common_name, config, subnet, route, local, proto, port, **paths): # Install dependencies @@ -443,32 +454,14 @@ def certidude_setup_openvpn_server(authority, common_name, config, subnet, route type=click.File(mode="w", atomic=True, lazy=True), help="Site configuration file of nginx, /etc/nginx/sites-available/%s.conf by default" % const.HOSTNAME) @click.option("--verify-client", "-vc", default="optional", type=click.Choice(['optional', 'on', 'off'])) +@fqdn_required @setup_client(prefix="server_", dh=True) def certidude_setup_nginx(authority, common_name, site_config, tls_config, verify_client, **paths): + apt("nginx") rpm("nginx") from jinja2 import Environment, PackageLoader env = Environment(loader=PackageLoader("certidude", "templates"), trim_blocks=True) - if "." not in common_name: - raise ValueError("Fully qualified hostname not specified as common name, make sure hostname -f works") - client_config = ConfigParser() - if os.path.exists(const.CLIENT_CONFIG_PATH): - client_config.readfp(open(const.CLIENT_CONFIG_PATH)) - 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", common_name) - client_config.set(authority, "request path", request_path) - client_config.set(authority, "key path", key_path) - client_config.set(authority, "certificate path", certificate_path) - client_config.set(authority, "authority path", authority_path) - client_config.set(authority, "revocations path", revocations_path) - with open(const.CLIENT_CONFIG_PATH + ".part", 'wb') as fh: - client_config.write(fh) - os.rename(const.CLIENT_CONFIG_PATH + ".part", const.CLIENT_CONFIG_PATH) - click.echo("Section '%s' added to %s" % (authority, const.CLIENT_CONFIG_PATH)) context = globals() # Grab const.BLAH context.update(locals()) @@ -507,7 +500,7 @@ def certidude_setup_nginx(authority, common_name, site_config, tls_config, verif type=click.File(mode="w", atomic=True, lazy=True), help="OpenVPN configuration file") @setup_client() -def certidude_setup_openvpn_client(authority, remote, common_name, config, proto, **ctx): +def certidude_setup_openvpn_client(authority, remote, common_name, config, proto, **paths): # Install dependencies apt("openvpn") rpm("openvpn") @@ -536,10 +529,10 @@ def certidude_setup_openvpn_client(authority, remote, common_name, config, proto config.write("proto %s\n" % proto) config.write("dev tun-%s\n" % remote.split(".")[0]) config.write("nobind\n") - config.write("key %s\n" % client_config.get(authority, "key path")) - config.write("cert %s\n" % client_config.get(authority, "certificate path")) - config.write("ca %s\n" % client_config.get(authority, "authority path")) - config.write("crl-verify %s\n" % client_config.get(authority, "revocations path")) + config.write("key %s\n" % paths.get("key path")) + config.write("cert %s\n" % paths.get("certificate path")) + config.write("ca %s\n" % paths.get("authority path")) + config.write("crl-verify %s\n" % paths.get("revocations path")) config.write("comp-lzo\n") config.write("user nobody\n") config.write("group nogroup\n") @@ -559,11 +552,9 @@ def certidude_setup_openvpn_client(authority, remote, common_name, config, proto @click.option("--common-name", "-cn", default=const.FQDN, help="Common name, %s by default" % const.FQDN) @click.option("--subnet", "-sn", default=u"192.168.33.0/24", type=ip_network, help="IPsec virtual subnet, 192.168.33.0/24 by default") @click.option("--route", "-r", type=ip_network, multiple=True, help="Subnets to advertise via this connection, multiple allowed") +@fqdn_required @setup_client(prefix="server_") def certidude_setup_strongswan_server(authority, common_name, subnet, route, **paths): - if "." not in common_name: - raise ValueError("Hostname has to be fully qualified!") - # Install dependencies apt("strongswan") rpm("strongswan") @@ -729,11 +720,8 @@ def certidude_setup_openvpn_networkmanager(authority, remote, common_name, **pat @click.option("--directory", help="Directory for authority files") @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) +@fqdn_required 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): - if "." not in common_name: - raise ValueError("No FQDN configured on this system!") - click.echo("Using fully qualified hostname: %s" % common_name) - # 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") diff --git a/tests/test_cli.py b/tests/test_cli.py index d3d9741..09eeac5 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -101,6 +101,10 @@ def test_cli_setup_authority(): if os.path.exists("/var/log/certidude.log"): os.unlink("/var/log/certidude.log") + # systemd + if os.path.exists("/etc/systemd/system/certidude.service"): + os.unlink("/etc/systemd/system/certidude.service") + # Remove nginx stuff if os.path.exists("/etc/nginx/sites-available/ca.conf"): os.unlink("/etc/nginx/sites-available/ca.conf") @@ -125,7 +129,11 @@ def test_cli_setup_authority(): from certidude.cli import entry_point as cli from certidude import const - result = runner.invoke(cli, ['setup', 'authority']) + result = runner.invoke(cli, ['setup', 'authority', '-s']) + os.setgid(0) # Restore GID + os.umask(0022) + + result = runner.invoke(cli, ['setup', 'authority']) # For if-else branches os.setgid(0) # Restore GID os.umask(0022) @@ -472,9 +480,15 @@ def test_cli_setup_authority(): ############# clean_client() + result = runner.invoke(cli, ["setup", "nginx", "-cn", "www", "ca.example.lan"]) + assert result.exception # FQDN required + result = runner.invoke(cli, ["setup", "nginx", "-cn", "www.example.lan", "ca.example.lan"]) assert not result.exception, result.output + result = runner.invoke(cli, ["setup", "nginx", "-cn", "www.example.lan", "ca.example.lan"]) + assert not result.exception, result.output # blah already exists, remove to regenerate + import os with open("/etc/certidude/client.conf", "a") as fh: @@ -482,6 +496,7 @@ def test_cli_setup_authority(): result = runner.invoke(cli, ["request", "--no-wait"]) assert not result.exception, result.output + assert "refused to sign" in result.output, result.output child_pid = os.fork() if not child_pid: @@ -512,9 +527,15 @@ def test_cli_setup_authority(): if not os.path.exists("/etc/openvpn/keys"): os.makedirs("/etc/openvpn/keys") + result = runner.invoke(cli, ['setup', 'openvpn', 'server', "-cn", "vpn", "ca.example.lan"]) + assert result.exception, result.output + result = runner.invoke(cli, ['setup', 'openvpn', 'server', "-cn", "vpn.example.lan", "ca.example.lan"]) assert not result.exception, result.output + result = runner.invoke(cli, ['setup', 'openvpn', 'server', "-cn", "vpn.example.lan", "ca.example.lan"]) + assert not result.exception, result.output # blah already exists, remove to regenerate + with open("/etc/certidude/client.conf", "a") as fh: fh.write("insecure = true\n") @@ -541,6 +562,9 @@ def test_cli_setup_authority(): result = runner.invoke(cli, ['setup', 'openvpn', 'client', "-cn", "roadwarrior1", "ca.example.lan", "vpn.example.lan"]) assert not result.exception, result.output + result = runner.invoke(cli, ['setup', 'openvpn', 'client', "-cn", "roadwarrior1", "ca.example.lan", "vpn.example.lan"]) + assert not result.exception, result.output # blah already exists, remove to regenerate + with open("/etc/certidude/client.conf", "a") as fh: fh.write("insecure = true\n") @@ -556,9 +580,15 @@ def test_cli_setup_authority(): clean_client() + result = runner.invoke(cli, ['setup', 'strongswan', 'server', "-cn", "ipsec", "ca.example.lan"]) + assert result.exception, result.output # FQDN required + result = runner.invoke(cli, ['setup', 'strongswan', 'server', "-cn", "ipsec.example.lan", "ca.example.lan"]) assert not result.exception, result.output + result = runner.invoke(cli, ['setup', 'strongswan', 'server', "-cn", "ipsec.example.lan", "ca.example.lan"]) + assert not result.exception, result.output # blah already exists, remove to regenerate + with open("/etc/certidude/client.conf", "a") as fh: fh.write("insecure = true\n") @@ -585,6 +615,9 @@ def test_cli_setup_authority(): result = runner.invoke(cli, ['setup', 'strongswan', 'client', "-cn", "roadwarrior2", "ca.example.lan", "ipsec.example.lan"]) assert not result.exception, result.output + result = runner.invoke(cli, ['setup', 'strongswan', 'client', "-cn", "roadwarrior2", "ca.example.lan", "ipsec.example.lan"]) + assert not result.exception, result.output # blah already exists, remove to regenerate + with open("/etc/certidude/client.conf", "a") as fh: fh.write("insecure = true\n") @@ -597,12 +630,31 @@ def test_cli_setup_authority(): ### NetworkManager ### ###################### + clean_client() + result = runner.invoke(cli, ['setup', 'openvpn', 'networkmanager', "-cn", "roadwarrior3", "ca.example.lan", "vpn.example.lan"]) assert not result.exception, result.output + with open("/etc/certidude/client.conf", "a") as fh: + fh.write("insecure = true\n") + + result = runner.invoke(cli, ["request", "--no-wait"]) + assert not result.exception, result.output + assert "Writing certificate to:" in result.output, result.output + + clean_client() + result = runner.invoke(cli, ['setup', 'strongswan', 'networkmanager', "-cn", "roadwarrior4", "ca.example.lan", "ipsec.example.lan"]) assert not result.exception, result.output + with open("/etc/certidude/client.conf", "a") as fh: + fh.write("insecure = true\n") + + result = runner.invoke(cli, ["request", "--no-wait"]) + assert not result.exception, result.output + assert "Writing certificate to:" in result.output, result.output + + ################### ### Final tests ###