mirror of
				https://github.com/laurivosandi/certidude
				synced 2025-10-31 01:19:11 +00:00 
			
		
		
		
	Allow provisioning as subordinate CA and add offline install docs
This commit is contained in:
		
							
								
								
									
										93
									
								
								README.rst
									
									
									
									
									
								
							
							
						
						
									
										93
									
								
								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 <https://tools.ietf.org/html/rfc4557>`_ and `SCEP <https://tools.ietf.org/html/draft-nourse-scep-23>`_ 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 | ||||
|   | ||||
| @@ -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") | ||||
|  | ||||
|  | ||||
| def get_request(common_name): | ||||
|   | ||||
| @@ -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,6 +1167,9 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, organizat | ||||
|             click.echo("Installing JavaScript packages: %s" % cmd) | ||||
|             if os.system(cmd): sys.exit(230) | ||||
|  | ||||
|         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) | ||||
| @@ -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") | ||||
|   | ||||
| @@ -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") | ||||
|   | ||||
| @@ -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/ { | ||||
|   | ||||
| @@ -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/ | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user