From f4627b3bd63a1f86daa6de097f280a0838be5eda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauri=20V=C3=B5sandi?= Date: Mon, 7 May 2018 11:18:29 +0000 Subject: [PATCH] Allow provisioning as subordinate CA and add offline install docs --- README.rst | 93 +++++++++--------- certidude/authority.py | 17 ++-- certidude/cli.py | 125 +++++++++++++++---------- certidude/config.py | 1 + certidude/templates/server/nginx.conf | 6 +- certidude/templates/server/server.conf | 9 +- tests/test_cli.py | 58 ++++++------ 7 files changed, 175 insertions(+), 134 deletions(-) diff --git a/README.rst b/README.rst index 24d18f5..67d0255 100644 --- a/README.rst +++ b/README.rst @@ -60,6 +60,7 @@ Features Common: * Standard request, sign, revoke workflow via web interface. +* RSA and Elliptic Curve Cryptography both supported, use ``certidude setup authority --elliptic-curve`` for the second * `OCSP `_ and `SCEP `_ support. * PAM and Active Directory compliant authentication backends: Kerberos single sign-on, LDAP simple bind. * POSIX groups and Active Directory (LDAP) group membership based authorization. @@ -93,12 +94,8 @@ System dependencies for Ubuntu 16.04: .. code:: bash - apt install -y \ - python3-click \ - python3-jinja2 python3-markdown \ - python3-pip \ - python3-mysql.connector python3-requests \ - python3-pyxattr + apt install -y python3-click python3-jinja2 python3-markdown \ + python3-pip python3-mysql.connector python3-requests python3-pyxattr System dependencies for Fedora 25+: @@ -122,7 +119,6 @@ You can check it with: hostname -f The command should return ``ca.example.com``. - If necessary tweak machine's fully qualified hostname in ``/etc/hosts``: .. code:: @@ -130,8 +126,15 @@ If necessary tweak machine's fully qualified hostname in ``/etc/hosts``: 127.0.0.1 localhost 127.0.1.1 ca.example.com ca +Certidude will submit e-mail notifications to locally running MTA. +Install Postfix and configure it as Satellite system: + +.. code:: bash + + apt install postfix + Certidude can set up certificate authority relatively easily. -Following will set up certificate authority in ``/var/lib/certidude/hostname.domain.tld``, +Following will set up certificate authority in ``/var/lib/certidude/``, configure systemd service for your platform, nginx in ``/etc/nginx/sites-available/certidude.conf``, cronjobs in ``/etc/cron.hourly/certidude`` and much more: @@ -140,20 +143,13 @@ cronjobs in ``/etc/cron.hourly/certidude`` and much more: certidude setup authority -Tweak the configuration in ``/etc/certidude/server.conf`` until you meet your requirements -and start the services: +Tweak the configuration in ``/etc/certidude/server.conf`` until you meet your requirements, +to apply changes run: .. code:: bash systemctl restart certidude -Certidude will submit e-mail notifications to locally running MTA. -Install Postfix and configure it as Satellite system: - -.. code:: bash - - apt install postfix - Setting up PAM authentication ----------------------------- @@ -247,7 +243,6 @@ Setting up services ------------------- Set up services as usual (OpenVPN, Strongswan, etc), when setting up certificates -generate signing request with TLS server flag set. See Certidude admin interface how to submit CSR-s and retrieve signed certificates. @@ -262,26 +257,31 @@ Configure Certidude client in ``/etc/certidude/client.conf``: .. code:: ini [ca.example.com] - insecure = true trigger = interface up + hostname = $HOSTNAME Configure services in ``/etc/certidude/services.conf``: .. code:: bash - [gateway.example.com] + [OpenVPN to gateway.example.com] authority = ca.example.com service = network-manager/openvpn remote = gateway.example.com + [IPSec to gateway.example.com] + authority = ca.example.com + service = network-manager/strongswan + remote = gateway.example.com + To request certificate: .. code:: bash - certidude request + certidude enroll The keys, signing requests, certificates and CRL-s are placed under -/var/lib/certidude/ca.example.com/ +/etc/certidude/authority/ca.example.com/ The VPN connection should immideately become available under network connections. @@ -293,10 +293,8 @@ To use dependencies from pip: .. code:: bash - apt install \ - build-essential python-dev cython libffi-dev libssl-dev libkrb5-dev \ - ldap-utils krb5-user \ - libsasl2-modules-gssapi-mit \ + apt install build-essential python-dev cython libffi-dev libssl-dev \ + libkrb5-dev ldap-utils krb5-user libsasl2-modules-gssapi-mit \ libsasl2-dev libldap2-dev Clone the repository: @@ -324,7 +322,7 @@ To run tests and measure code coverage grab a clean VM or container: pip3 install codecov pytest-cov rm .coverage* - TRAVIS=1 coverage run --parallel-mode --source certidude -m py.test tests + COVERAGE_FILE=/tmp/.coverage TRAVIS=1 coverage run --parallel-mode --source certidude -m py.test tests --capture=sys coverage combine coverage report @@ -335,23 +333,34 @@ To uninstall: pip3 uninstall certidude -Certificate attributes ----------------------- +Offline install +--------------- -Certificates have a lot of fields that can be filled in. -In any case country, state, locality, organization, organizational unit are not filled in -as this information will already exist in AD and duplicating it in the certificate management -doesn't make sense. Additionally the information will get out of sync if -attributes are changed in AD but certificates won't be updated. +To set up certificate authority in an isolated environment use a +vanilla Ubuntu 16.04 or container to collect the artifacts: -If machine is enrolled, eg by running ``certidude request`` as root on Ubuntu/Fedora/Mac OS X: +.. code:: bash -* If Kerberos credentials are presented machine can be automatically enrolled depending on the ``machine enrollment`` setting -* Common name is set to short ``hostname`` -* It is tricky to determine user who is triggering the action so given name, surname and e-mail attributes are not filled in + add-apt-repository -y ppa:nginx/stable + apt-get update -q + rm -fv /var/cache/apt/archives/*.deb /var/cache/certidude/wheels/*.whl + apt install --download-only python3-markdown python3-pyxattr python3-jinja2 python3-cffi software-properties-common libnginx-mod-nchan nginx-full + pip3 wheel --wheel-dir=/var/cache/certidude/wheels -r requirements.txt + pip3 wheel --wheel-dir=/var/cache/certidude/wheels falcon humanize ipaddress simplepam user-agents python-ldap gssapi + pip3 wheel --wheel-dir=/var/cache/certidude/wheels . + tar -cf certidude-assets.tar /var/lib/certidude/assets/ /var/cache/apt/archives/ /var/cache/certidude/wheels -If user enrolls, eg by clicking generate bundle button in the web interface: +Transfer certidude-artifacts.tar to the target machine and execute: -* Common name is either set to ``username`` or ``username@device-identifier`` depending on the ``user enrollment`` setting -* Given name and surname are not filled in because Unicode characters cause issues in OpenVPN Connect app -* E-mail is not filled in because it might change in AD +.. code:: bash + + rm -fv /var/cache/apt/archives/*.deb /var/cache/certidude/wheels/*.whl + tar -xvf certidude-artifacts.tar -C / + dpkg -i /var/cache/apt/archives/*.deb + pip3 install --use-wheel --no-index --find-links /var/cache/certidude/wheels/*.whl + +Proceed to bootstrap authority without installing packages or assembling assets: + + certidude setup authority --skip-packages --skip-assets [--elliptic-curve] [--organization "Mycorp LLC"] + +Note it's highly recommended to enable nginx PPA in the target machine diff --git a/certidude/authority.py b/certidude/authority.py index 5066edb..ef538b5 100644 --- a/certidude/authority.py +++ b/certidude/authority.py @@ -53,17 +53,15 @@ with open(config.AUTHORITY_PRIVATE_KEY_PATH, "rb") as fh: def self_enroll(skip_notify=False): assert os.getuid() == 0 and os.getgid() == 0, "Can self-enroll only as root" - from certidude import const + from certidude import const, config common_name = const.FQDN - directory = os.path.join("/var/lib/certidude", const.FQDN) - self_key_path = os.path.join(directory, "self_key.pem") try: path, buf, cert, signed, expires = get_signed(common_name) self_public_key = asymmetric.load_public_key(path) - private_key = asymmetric.load_private_key(self_key_path) + private_key = asymmetric.load_private_key(config.SELF_KEY_PATH) except FileNotFoundError: # certificate or private key not found - with open(self_key_path, 'wb') as fh: + with open(config.SELF_KEY_PATH, 'wb') as fh: if public_key.algorithm == "ec": self_public_key, private_key = asymmetric.generate_pair("ec", curve=public_key.curve) elif public_key.algorithm == "rsa": @@ -81,11 +79,11 @@ def self_enroll(skip_notify=False): request = builder.build(private_key) pid = os.fork() if not pid: - from certidude import authority + from certidude import authority, config from certidude.common import drop_privileges drop_privileges() assert os.getuid() != 0 and os.getgid() != 0 - path = os.path.join(directory, "requests", common_name + ".pem") + path = os.path.join(config.REQUESTS_DIR, common_name + ".pem") click.echo("Writing request to %s" % path) with open(path, "wb") as fh: fh.write(pem_armor_csr(request)) # Write CSR with certidude permissions @@ -93,10 +91,7 @@ def self_enroll(skip_notify=False): sys.exit(0) else: os.waitpid(pid, 0) - if os.path.exists("/etc/systemd"): - os.system("systemctl reload nginx") - else: - os.system("service nginx reload") + os.system("systemctl reload nginx") def get_request(common_name): diff --git a/certidude/cli.py b/certidude/cli.py index 3815479..01d7eb2 100755 --- a/certidude/cli.py +++ b/certidude/cli.py @@ -1001,12 +1001,14 @@ def certidude_setup_openvpn_networkmanager(authority, remote, common_name, **pat @click.option("--organization", "-o", default=None, help="Company or organization name") @click.option("--organizational-unit", "-ou", default="Certificate Authority") @click.option("--push-server", help="Push server, by default http://%s" % const.FQDN) -@click.option("--directory", help="Directory for authority files") +@click.option("--directory", default="/var/lib/certidude", help="Directory for authority files") @click.option("--outbox", default="smtp://smtp.%s" % const.DOMAIN, help="SMTP server, smtp://smtp.%s by default" % const.DOMAIN) +@click.option("--skip-assets", is_flag=True, help="Don't attempt to assemble JS/CSS/font assets") @click.option("--skip-packages", is_flag=True, help="Don't attempt to install apt/pip/npm packages") @click.option("--elliptic-curve", "-e", is_flag=True, help="Generate EC instead of RSA keypair") +@click.option("--subordinate", is_flag=True, help="Set up subordinate CA instead of root CA") @fqdn_required -def certidude_setup_authority(username, kerberos_keytab, nginx_config, organization, organizational_unit, common_name, directory, authority_lifetime, push_server, outbox, title, skip_packages, elliptic_curve): +def certidude_setup_authority(username, kerberos_keytab, nginx_config, organization, organizational_unit, common_name, directory, authority_lifetime, push_server, outbox, title, skip_assets, skip_packages, elliptic_curve, subordinate): assert subprocess.check_output(["/usr/bin/lsb_release", "-cs"]) in (b"trusty\n", b"xenial\n", b"bionic\n"), "Only Ubuntu 16.04 supported at the moment" assert os.getuid() == 0 and os.getgid() == 0, "Authority can be set up only by root" @@ -1052,8 +1054,6 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, organizat template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates", "profile") click.echo("Using templates from %s" % template_path) - if not directory: - directory = os.path.join("/var/lib/certidude", common_name) click.echo("Placing authority files in %s" % directory) certificate_url = "http://%s/api/certificate/" % common_name @@ -1065,8 +1065,11 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, organizat # Expand variables assets_dir = os.path.join(directory, "assets") ca_key = os.path.join(directory, "ca_key.pem") + ca_req = os.path.join(directory, "ca_req.pem") ca_cert = os.path.join(directory, "ca_cert.pem") + self_key = os.path.join(directory, "self_key.pem") sqlite_path = os.path.join(directory, "meta", "db.sqlite") + distinguished_name = cn_to_dn("Certidude at %s" % common_name, common_name, o=organization, ou=organizational_unit) # Builder variables dhgroup = "ecp384" if elliptic_curve else "modp2048" @@ -1164,35 +1167,38 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, organizat click.echo("Installing JavaScript packages: %s" % cmd) if os.system(cmd): sys.exit(230) - # Copy fonts - click.echo("Copying fonts...") - if os.system("rsync -avq /usr/local/lib/node_modules/font-awesome/fonts/ %s/fonts/" % assets_dir): sys.exit(229) + if skip_assets: + click.echo("Not attempting to assemble assets as requested...") + else: + # Copy fonts + click.echo("Copying fonts...") + if os.system("rsync -avq /usr/local/lib/node_modules/font-awesome/fonts/ %s/fonts/" % assets_dir): sys.exit(229) - # Compile nunjucks templates - cmd = 'nunjucks-precompile --include ".html$" --include ".ps1$" --include ".sh$" --include ".svg" %s > %s.part' % (static_path, bundle_js) - click.echo("Compiling templates: %s" % cmd) - if os.system(cmd): sys.exit(228) + # Compile nunjucks templates + cmd = 'nunjucks-precompile --include ".html$" --include ".ps1$" --include ".sh$" --include ".svg" %s > %s.part' % (static_path, bundle_js) + click.echo("Compiling templates: %s" % cmd) + if os.system(cmd): sys.exit(228) - # Assemble bundle.js - click.echo("Assembling %s" % bundle_js) - with open(bundle_js + ".part", "a") as fh: - for pkg in "qrcode-svg/dist/qrcode.min.js", "jquery/dist/jquery.min.js", "timeago/*.js", "nunjucks/browser/nunjucks-slim.min.js", "tether/dist/js/*.min.js", "bootstrap/dist/js/*.min.js": - for j in glob(os.path.join("/usr/local/lib/node_modules", pkg)): - click.echo("- Merging: %s" % j) - with open(j) as ih: - fh.write(ih.read()) + # Assemble bundle.js + click.echo("Assembling %s" % bundle_js) + with open(bundle_js + ".part", "a") as fh: + for pkg in "qrcode-svg/dist/qrcode.min.js", "jquery/dist/jquery.min.js", "timeago/*.js", "nunjucks/browser/nunjucks-slim.min.js", "tether/dist/js/*.min.js", "bootstrap/dist/js/*.min.js": + for j in glob(os.path.join("/usr/local/lib/node_modules", pkg)): + click.echo("- Merging: %s" % j) + with open(j) as ih: + fh.write(ih.read()) - # Assemble bundle.css - click.echo("Assembling %s" % bundle_css) - with open(bundle_css + ".part", "w") as fh: - for pkg in "tether/dist/css/*.min.css", "bootstrap/dist/css/*.min.*css", "font-awesome/css/font-awesome.min.css": - for j in glob(os.path.join("/usr/local/lib/node_modules", pkg)): - click.echo("- Merging: %s" % j) - with open(j) as ih: - fh.write(ih.read()) + # Assemble bundle.css + click.echo("Assembling %s" % bundle_css) + with open(bundle_css + ".part", "w") as fh: + for pkg in "tether/dist/css/*.min.css", "bootstrap/dist/css/*.min.*css", "font-awesome/css/font-awesome.min.css": + for j in glob(os.path.join("/usr/local/lib/node_modules", pkg)): + click.echo("- Merging: %s" % j) + with open(j) as ih: + fh.write(ih.read()) - os.rename(bundle_css + ".part", bundle_css) - os.rename(bundle_js + ".part", bundle_js) + os.rename(bundle_css + ".part", bundle_css) + os.rename(bundle_js + ".part", bundle_js) assert os.getuid() == 0 and os.getgid() == 0 _, _, uid, gid, gecos, root, shell = pwd.getpwnam("certidude") @@ -1203,7 +1209,8 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, organizat click.echo("Creating %s" % const.CONFIG_DIR) os.makedirs(const.CONFIG_DIR) - os.umask(0o137) # 640 + os.umask(0o177) # 600 + if os.path.exists(const.SERVER_CONFIG_PATH): click.echo("Configuration file %s already exists, remove to regenerate" % const.SERVER_CONFIG_PATH) else: @@ -1250,7 +1257,7 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, organizat pass # Generate and sign CA key - if not os.path.exists(ca_key): + if not os.path.exists(ca_key) or subordinate and not os.path.exists(ca_req): if elliptic_curve: click.echo("Generating %s EC key for CA ..." % const.CURVE_NAME) public_key, private_key = asymmetric.generate_pair("ec", curve=const.CURVE_NAME) @@ -1258,12 +1265,38 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, organizat click.echo("Generating %d-bit RSA key for CA ..." % const.KEY_SIZE) public_key, private_key = asymmetric.generate_pair("rsa", bit_size=const.KEY_SIZE) + # Set permission bits to 600 + os.umask(0o177) + with open(ca_key, 'wb') as f: + f.write(asymmetric.dump_private_key(private_key, None)) + + if subordinate: + builder = CSRBuilder(distinguished_name, public_key) + request = builder.build(private_key) + with open(ca_req + ".part", 'wb') as f: + f.write(pem_armor_csr(request)) + os.rename(ca_req + ".part", ca_req) + + if not os.path.exists(ca_cert): + if subordinate: + click.echo("Request has been written to %s" % ca_req) + click.echo() + click.echo(open(ca_req).read()) + click.echo() + click.echo("Get it signed and insert signed certificate into %s" % ca_cert) + click.echo() + click.echo(" cat > %s" % ca_cert) + click.echo() + click.echo("Paste contents and press Ctrl-D, adjust permissions:") + click.echo() + click.echo(" chown root:root %s" % ca_cert) + click.echo(" chmod 0644 %s" % ca_cert) + click.echo() + click.echo("To finish setup procedure run 'certidude setup authority' again") + sys.exit(1) + # https://technet.microsoft.com/en-us/library/aa998840(v=exchg.141).aspx - builder = CertificateBuilder( - cn_to_dn("Certidude at %s" % common_name, common_name, - o=organization, ou=organizational_unit), - public_key - ) + builder = CertificateBuilder(distinguished_name, public_key) builder.self_signed = True builder.ca = True builder.serial_number = random.randint( @@ -1280,22 +1313,20 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, organizat with open(ca_cert, 'wb') as f: f.write(pem_armor_certificate(certificate)) - # Set permission bits to 600 - os.umask(0o177) - with open(ca_key, 'wb') as f: - f.write(asymmetric.dump_private_key(private_key, None)) - sys.exit(0) # stop this fork here - - assert os.stat(sqlite_path).st_mode == 0o100640 - assert os.stat(ca_cert).st_mode == 0o100640 - assert os.stat(ca_key).st_mode == 0o100600 - assert os.stat("/etc/nginx/sites-available/certidude.conf").st_mode == 0o100640 else: - os.waitpid(bootstrap_pid, 0) + _, exitcode = os.waitpid(bootstrap_pid, 0) + if exitcode: + return 0 from certidude import authority authority.self_enroll(skip_notify=True) assert os.getuid() == 0 and os.getgid() == 0, "Enroll contaminated environment" + assert os.stat(sqlite_path).st_mode == 0o100660 + assert os.stat(ca_cert).st_mode == 0o100640 + assert os.stat(ca_key).st_mode == 0o100600 + assert os.stat("/etc/nginx/sites-available/certidude.conf").st_mode == 0o100600 + assert os.stat("/etc/certidude/server.conf").st_mode == 0o100600 + click.echo("Enabling and starting Certidude backend") os.system("systemctl enable certidude") os.system("systemctl restart certidude") diff --git a/certidude/config.py b/certidude/config.py index e6d2434..89bf619 100644 --- a/certidude/config.py +++ b/certidude/config.py @@ -50,6 +50,7 @@ KERBEROS_SUBNETS = set([ipaddress.ip_network(j) for j in AUTHORITY_DIR = "/var/lib/certidude" AUTHORITY_PRIVATE_KEY_PATH = cp.get("authority", "private key path") AUTHORITY_CERTIFICATE_PATH = cp.get("authority", "certificate path") +SELF_KEY_PATH = cp.get("authority", "self key path") REQUESTS_DIR = cp.get("authority", "requests dir") SIGNED_DIR = cp.get("authority", "signed dir") SIGNED_BY_SERIAL_DIR = os.path.join(SIGNED_DIR, "by-serial") diff --git a/certidude/templates/server/nginx.conf b/certidude/templates/server/nginx.conf index b27f17b..40a2820 100644 --- a/certidude/templates/server/nginx.conf +++ b/certidude/templates/server/nginx.conf @@ -23,8 +23,8 @@ send_timeout 600; nchan_message_buffer_length 0; # To use CA-s own certificate for frontend and mutually authenticated connections -ssl_certificate /var/lib/certidude/{{ common_name }}/signed/{{ common_name }}.pem; -ssl_certificate_key /var/lib/certidude/{{common_name}}/self_key.pem; +ssl_certificate {{ directory }}/signed/{{ common_name }}.pem; +ssl_certificate_key {{ directory }}/self_key.pem; server { # Section for serving insecure HTTP, note that this is suitable for @@ -138,7 +138,7 @@ server { # Allow client authentication with certificate, # backend must still check if certificate was used for TLS handshake ssl_verify_client optional; - ssl_client_certificate /var/lib/certidude/{{ common_name }}/ca_cert.pem; + ssl_client_certificate {{ directory }}/ca_cert.pem; # Proxy pass to backend location /api/ { diff --git a/certidude/templates/server/server.conf b/certidude/templates/server/server.conf index 8f8b670..e97869e 100644 --- a/certidude/templates/server/server.conf +++ b/certidude/templates/server/server.conf @@ -25,9 +25,9 @@ kerberos realm = EXAMPLE.LAN {% if domain %} # LDAP URI derived from /etc/samba/smb.conf -ldap uri = ldap://dc1.{{ domain }} +ldap uri = ldaps://dc1.{{ domain }} {% else %} -# LDAP URI +# Placeholder LDAP URI ldap uri = ldaps://dc1.example.lan {% endif %} @@ -223,9 +223,14 @@ request submission allowed = false ;user enrollment = single allowed user enrollment = multiple allowed +# Certificate authority keypair private key path = {{ ca_key }} certificate path = {{ ca_cert }} +# Private key used by nginx frontend +self key path = {{ self_key }} + +# Directories for requests, signed, revoked and expired certificates requests dir = {{ directory }}/requests/ signed dir = {{ directory }}/signed/ revoked dir = {{ directory }}/revoked/ diff --git a/tests/test_cli.py b/tests/test_cli.py index 46f14c1..ce849da 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -96,8 +96,8 @@ def clean_server(): pass - if os.path.exists("/var/lib/certidude/ca.example.lan"): - shutil.rmtree("/var/lib/certidude/ca.example.lan") + if os.path.exists("/var/lib/certidude"): + shutil.rmtree("/var/lib/certidude") if os.path.exists("/run/certidude"): shutil.rmtree("/run/certidude") @@ -230,13 +230,13 @@ def test_cli_setup_authority(): assert authority.public_key.algorithm == "ec" # Generate garbage - with open("/var/lib/certidude/ca.example.lan/bla", "w") as fh: + with open("/var/lib/certidude/bla", "w") as fh: pass - with open("/var/lib/certidude/ca.example.lan/requests/bla", "w") as fh: + with open("/var/lib/certidude/requests/bla", "w") as fh: pass - with open("/var/lib/certidude/ca.example.lan/signed/bla", "w") as fh: + with open("/var/lib/certidude/signed/bla", "w") as fh: pass - with open("/var/lib/certidude/ca.example.lan/revoked/bla", "w") as fh: + with open("/var/lib/certidude/revoked/bla", "w") as fh: pass # Start server before any signing operations are performed @@ -255,7 +255,7 @@ def test_cli_setup_authority(): # Test CA certificate fetch - buf = open("/var/lib/certidude/ca.example.lan/ca_cert.pem").read() + buf = open("/var/lib/certidude/ca_cert.pem").read() r = requests.get("http://ca.example.lan/api/certificate") assert r.status_code == 200 assert r.headers.get('content-type') == "application/x-x509-ca-cert" @@ -308,7 +308,7 @@ def test_cli_setup_authority(): headers={"content-type":"application/pkcs10"}) assert r.status_code == 202 # success assert "Stored request " in inbox.pop(), inbox - assert os.path.exists("/var/lib/certidude/ca.example.lan/requests/test.pem") + assert os.path.exists("/var/lib/certidude/requests/test.pem") # Test request deletion r = client().simulate_delete("/api/request/test/") @@ -319,7 +319,7 @@ def test_cli_setup_authority(): r = client().simulate_delete("/api/request/test/", headers={"User-Agent":UA_FEDORA_FIREFOX, "Authorization":admintoken}) assert r.status_code == 403, r.text # CSRF prevented - assert os.path.exists("/var/lib/certidude/ca.example.lan/requests/test.pem") + assert os.path.exists("/var/lib/certidude/requests/test.pem") r = client().simulate_delete("/api/request/test/", headers={"Authorization":admintoken}) assert r.status_code == 200, r.text @@ -507,19 +507,19 @@ def test_cli_setup_authority(): r = client().simulate_post("/api/lease/", query_string = "client=test&inner_address=127.0.0.1&outer_address=8.8.8.8", - headers={"X-SSL-CERT":open("/var/lib/certidude/ca.example.lan/signed/ca.example.lan.pem").read() }) + headers={"X-SSL-CERT":open("/var/lib/certidude/signed/ca.example.lan.pem").read() }) assert r.status_code == 200, r.text # lease update ok # Attempt to fetch and execute default.sh script from xattr import listxattr, getxattr - assert not [j for j in listxattr("/var/lib/certidude/ca.example.lan/signed/test.pem") if j.startswith(b"user.machine.")] + assert not [j for j in listxattr("/var/lib/certidude/signed/test.pem") if j.startswith(b"user.machine.")] #os.system("curl http://ca.example.lan/api/signed/test/script | bash") r = client().simulate_post("/api/signed/test/attr", body="cpu=i5&mem=512M&dist=Ubunt", headers={"content-type": "application/x-www-form-urlencoded"}) assert r.status_code == 200, r.text - assert getxattr("/var/lib/certidude/ca.example.lan/signed/test.pem", "user.machine.cpu") == b"i5" - assert getxattr("/var/lib/certidude/ca.example.lan/signed/test.pem", "user.machine.mem") == b"512M" - assert getxattr("/var/lib/certidude/ca.example.lan/signed/test.pem", "user.machine.dist") == b"Ubunt" + assert getxattr("/var/lib/certidude/signed/test.pem", "user.machine.cpu") == b"i5" + assert getxattr("/var/lib/certidude/signed/test.pem", "user.machine.mem") == b"512M" + assert getxattr("/var/lib/certidude/signed/test.pem", "user.machine.dist") == b"Ubunt" # Test tagging integration in scripting framework r = client().simulate_get("/api/signed/test/script/") @@ -572,11 +572,11 @@ def test_cli_setup_authority(): # Test lease update r = client().simulate_post("/api/lease/", query_string = "client=test&inner_address=127.0.0.1&outer_address=8.8.8.8&serial=0", - headers={"X-SSL-CERT":open("/var/lib/certidude/ca.example.lan/signed/ca.example.lan.pem").read() }) + headers={"X-SSL-CERT":open("/var/lib/certidude/signed/ca.example.lan.pem").read() }) assert r.status_code == 403, r.text # invalid serial number supplied r = client().simulate_post("/api/lease/", query_string = "client=test&inner_address=1.2.3.4&outer_address=8.8.8.8", - headers={"X-SSL-CERT":open("/var/lib/certidude/ca.example.lan/signed/ca.example.lan.pem").read() }) + headers={"X-SSL-CERT":open("/var/lib/certidude/signed/ca.example.lan.pem").read() }) assert r.status_code == 200, r.text # lease update ok @@ -717,11 +717,11 @@ def test_cli_setup_authority(): assert not result.exception, result.output assert "(autosign not requested)" in result.output, result.output assert not os.path.exists("/run/certidude/ca.example.lan.pid"), result.output - assert not os.path.exists("/var/lib/certidude/ca.example.lan/signed/vpn.example.lan.pem") + assert not os.path.exists("/var/lib/certidude/signed/vpn.example.lan.pem") child_pid = os.fork() if not child_pid: - assert not os.path.exists("/var/lib/certidude/ca.example.lan/signed/vpn.example.lan.pem") + assert not os.path.exists("/var/lib/certidude/signed/vpn.example.lan.pem") result = runner.invoke(cli, ["sign", "vpn.example.lan", "--profile", "srv"]) assert not result.exception, result.output assert "overwrit" not in result.output, result.output @@ -912,20 +912,20 @@ def test_cli_setup_authority(): # Setup gateway clean_client() - assert not os.path.exists("/var/lib/certidude/ca.example.lan/signed/ipsec.example.lan.pem") + assert not os.path.exists("/var/lib/certidude/signed/ipsec.example.lan.pem") result = runner.invoke(cli, ['setup', 'strongswan', 'server', "-cn", "ipsec", "ca.example.lan"]) assert result.exception, result.output # FQDN required - assert not os.path.exists("/var/lib/certidude/ca.example.lan/signed/ipsec.example.lan.pem") + assert not os.path.exists("/var/lib/certidude/signed/ipsec.example.lan.pem") result = runner.invoke(cli, ['setup', 'strongswan', 'server', "-cn", "ipsec.example.lan", "ca.example.lan"]) assert not result.exception, result.output assert open("/etc/ipsec.secrets").read() == ": RSA /etc/certidude/authority/ca.example.lan/server_key.pem\n" - assert not os.path.exists("/var/lib/certidude/ca.example.lan/signed/ipsec.example.lan.pem") + assert not os.path.exists("/var/lib/certidude/signed/ipsec.example.lan.pem") result = runner.invoke(cli, ['setup', 'strongswan', 'server', "-cn", "ipsec.example.lan", "ca.example.lan"]) assert not result.exception, result.output # client conf already exists, remove to regenerate - assert not os.path.exists("/var/lib/certidude/ca.example.lan/signed/ipsec.example.lan.pem") + assert not os.path.exists("/var/lib/certidude/signed/ipsec.example.lan.pem") with open("/etc/certidude/client.conf", "a") as fh: fh.write("autosign = false\n") @@ -934,11 +934,11 @@ def test_cli_setup_authority(): assert not result.exception, result.output assert "(autosign not requested)" in result.output, result.output assert not os.path.exists("/run/certidude/ca.example.lan.pid"), result.output - assert not os.path.exists("/var/lib/certidude/ca.example.lan/signed/ipsec.example.lan.pem") + assert not os.path.exists("/var/lib/certidude/signed/ipsec.example.lan.pem") child_pid = os.fork() if not child_pid: - assert not os.path.exists("/var/lib/certidude/ca.example.lan/signed/ipsec.example.lan.pem") + assert not os.path.exists("/var/lib/certidude/signed/ipsec.example.lan.pem") result = runner.invoke(cli, ["sign", "ipsec.example.lan", "--profile", "srv"]) assert not result.exception, result.output assert "overwrit" not in result.output, result.output @@ -1024,13 +1024,13 @@ def test_cli_setup_authority(): assert r.status_code == 400 - assert os.system("openssl ocsp -issuer /var/lib/certidude/ca.example.lan/ca_cert.pem -CAfile /var/lib/certidude/ca.example.lan/ca_cert.pem -cert /var/lib/certidude/ca.example.lan/signed/roadwarrior2.pem -text -url http://ca.example.lan/api/ocsp/ -out /tmp/ocsp1.log") == 0 - assert os.system("openssl ocsp -issuer /var/lib/certidude/ca.example.lan/ca_cert.pem -CAfile /var/lib/certidude/ca.example.lan/ca_cert.pem -cert /var/lib/certidude/ca.example.lan/ca_cert.pem -text -url http://ca.example.lan/api/ocsp/ -out /tmp/ocsp2.log") == 0 + assert os.system("openssl ocsp -issuer /var/lib/certidude/ca_cert.pem -CAfile /var/lib/certidude/ca_cert.pem -cert /var/lib/certidude/signed/roadwarrior2.pem -text -url http://ca.example.lan/api/ocsp/ -out /tmp/ocsp1.log") == 0 + assert os.system("openssl ocsp -issuer /var/lib/certidude/ca_cert.pem -CAfile /var/lib/certidude/ca_cert.pem -cert /var/lib/certidude/ca_cert.pem -text -url http://ca.example.lan/api/ocsp/ -out /tmp/ocsp2.log") == 0 - for filename in os.listdir("/var/lib/certidude/ca.example.lan/revoked"): + for filename in os.listdir("/var/lib/certidude/revoked"): if not filename.endswith(".pem"): continue - assert os.system("openssl ocsp -issuer /var/lib/certidude/ca.example.lan/ca_cert.pem -CAfile /var/lib/certidude/ca.example.lan/ca_cert.pem -cert /var/lib/certidude/ca.example.lan/revoked/%s -text -url http://ca.example.lan/api/ocsp/ -out /tmp/ocsp3.log" % filename) == 0 + assert os.system("openssl ocsp -issuer /var/lib/certidude/ca_cert.pem -CAfile /var/lib/certidude/ca_cert.pem -cert /var/lib/certidude/revoked/%s -text -url http://ca.example.lan/api/ocsp/ -out /tmp/ocsp3.log" % filename) == 0 break with open("/tmp/ocsp1.log") as fh: @@ -1108,7 +1108,7 @@ def test_cli_setup_authority(): # Bootstrap authority - assert not os.path.exists("/var/lib/certidude/ca.example.lan/ca_key.pem") + assert not os.path.exists("/var/lib/certidude/ca_key.pem") assert os.system("certidude setup authority --skip-packages") == 0