mirror of
				https://github.com/laurivosandi/certidude
				synced 2025-10-31 01:19:11 +00:00 
			
		
		
		
	Refactor
* Remove given name and surname attributes because of issues with OpenVPN Connect * Remove e-mail attribute because of no reliable method of deriving usable address * Remove organizational unit attribute * Don't overwrite Kerberos cronjob during certidude setup authority * Enforce path_length=0 for disabling intermediate CA-s * Remove SAN attributes * Add configuration options for outbox sender name and address * Use common name attribute to derive signature flags * Use distinct pub/sub URL-s for long poll and event source
This commit is contained in:
		| @@ -59,7 +59,7 @@ class SessionResource(object): | |||||||
|                 user_mutliple_certificates=config.USER_MULTIPLE_CERTIFICATES, |                 user_mutliple_certificates=config.USER_MULTIPLE_CERTIFICATES, | ||||||
|                 outbox = config.OUTBOX, |                 outbox = config.OUTBOX, | ||||||
|                 certificate = authority.certificate, |                 certificate = authority.certificate, | ||||||
|                 events = config.PUSH_EVENT_SOURCE % config.PUSH_TOKEN, |                 events = config.EVENT_SOURCE_SUBSCRIBE % config.EVENT_SOURCE_TOKEN, | ||||||
|                 requests=authority.list_requests(), |                 requests=authority.list_requests(), | ||||||
|                 signed=authority.list_signed(), |                 signed=authority.list_signed(), | ||||||
|                 revoked=authority.list_revoked(), |                 revoked=authority.list_revoked(), | ||||||
|   | |||||||
| @@ -52,7 +52,7 @@ class RequestListResource(object): | |||||||
|                 raise falcon.HTTPBadRequest( |                 raise falcon.HTTPBadRequest( | ||||||
|                     "Bad request", |                     "Bad request", | ||||||
|                     "Common name %s differs from Kerberos credential %s!" % (csr.common_name, machine)) |                     "Common name %s differs from Kerberos credential %s!" % (csr.common_name, machine)) | ||||||
|             if csr.signable: |  | ||||||
|             # Automatic enroll with Kerberos machine cerdentials |             # Automatic enroll with Kerberos machine cerdentials | ||||||
|             resp.set_header("Content-Type", "application/x-x509-user-cert") |             resp.set_header("Content-Type", "application/x-x509-user-cert") | ||||||
|             resp.body = authority.sign(csr, overwrite=True).dump() |             resp.body = authority.sign(csr, overwrite=True).dump() | ||||||
| @@ -73,7 +73,7 @@ class RequestListResource(object): | |||||||
|         # TODO: check for revoked certificates and return HTTP 410 Gone |         # TODO: check for revoked certificates and return HTTP 410 Gone | ||||||
|  |  | ||||||
|         # Process automatic signing if the IP address is whitelisted, autosigning was requested and certificate can be automatically signed |         # Process automatic signing if the IP address is whitelisted, autosigning was requested and certificate can be automatically signed | ||||||
|         if req.get_param_as_bool("autosign") and csr.signable: |         if req.get_param_as_bool("autosign") and csr.is_client: | ||||||
|             for subnet in config.AUTOSIGN_SUBNETS: |             for subnet in config.AUTOSIGN_SUBNETS: | ||||||
|                 if req.context.get("remote_addr") in subnet: |                 if req.context.get("remote_addr") in subnet: | ||||||
|                     try: |                     try: | ||||||
| @@ -103,7 +103,7 @@ class RequestListResource(object): | |||||||
|         # Wait the certificate to be signed if waiting is requested |         # Wait the certificate to be signed if waiting is requested | ||||||
|         if req.get_param("wait"): |         if req.get_param("wait"): | ||||||
|             # Redirect to nginx pub/sub |             # Redirect to nginx pub/sub | ||||||
|             url = config.PUSH_LONG_POLL % csr.fingerprint() |             url = config.LONG_POLL_SUBSCRIBE % csr.fingerprint() | ||||||
|             click.echo("Redirecting to: %s"  % url) |             click.echo("Redirecting to: %s"  % url) | ||||||
|             resp.status = falcon.HTTP_SEE_OTHER |             resp.status = falcon.HTTP_SEE_OTHER | ||||||
|             resp.set_header("Location", url.encode("ascii")) |             resp.set_header("Location", url.encode("ascii")) | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ class RevocationListResource(object): | |||||||
|                 default_backend()).public_bytes(Encoding.DER) |                 default_backend()).public_bytes(Encoding.DER) | ||||||
|         elif req.client_accepts("application/x-pem-file"): |         elif req.client_accepts("application/x-pem-file"): | ||||||
|             if req.get_param_as_bool("wait"): |             if req.get_param_as_bool("wait"): | ||||||
|                 url = config.PUSH_LONG_POLL % "crl" |                 url = config.LONG_POLL_SUBSCRIBE % "crl" | ||||||
|                 resp.status = falcon.HTTP_SEE_OTHER |                 resp.status = falcon.HTTP_SEE_OTHER | ||||||
|                 resp.set_header("Location", url.encode("ascii")) |                 resp.set_header("Location", url.encode("ascii")) | ||||||
|                 logger.debug(u"Redirecting to CRL request to %s", url) |                 logger.debug(u"Redirecting to CRL request to %s", url) | ||||||
|   | |||||||
| @@ -31,11 +31,6 @@ def publish_certificate(func): | |||||||
|         cert = func(csr, *args, **kwargs) |         cert = func(csr, *args, **kwargs) | ||||||
|         assert isinstance(cert, Certificate), "notify wrapped function %s returned %s" % (func, type(cert)) |         assert isinstance(cert, Certificate), "notify wrapped function %s returned %s" % (func, type(cert)) | ||||||
|  |  | ||||||
|         if cert.given_name and cert.surname and cert.email_address: |  | ||||||
|             recipient = "%s %s <%s>" % (cert.given_name, cert.surname, cert.email_address) |  | ||||||
|         elif cert.email_address: |  | ||||||
|             recipient = cert.email_address |  | ||||||
|         else: |  | ||||||
|         recipient = None |         recipient = None | ||||||
|  |  | ||||||
|         mailer.send( |         mailer.send( | ||||||
| @@ -44,8 +39,8 @@ def publish_certificate(func): | |||||||
|             attachments=(cert,), |             attachments=(cert,), | ||||||
|             certificate=cert) |             certificate=cert) | ||||||
|  |  | ||||||
|         if config.PUSH_PUBLISH: |         if config.LONG_POLL_PUBLISH: | ||||||
|             url = config.PUSH_PUBLISH % csr.fingerprint() |             url = config.LONG_POLL_PUBLISH % csr.fingerprint() | ||||||
|             click.echo("Publishing certificate at %s ..." % url) |             click.echo("Publishing certificate at %s ..." % url) | ||||||
|             requests.post(url, data=cert.dump(), |             requests.post(url, data=cert.dump(), | ||||||
|                 headers={"User-Agent": "Certidude API", "Content-Type": "application/x-x509-user-cert"}) |                 headers={"User-Agent": "Certidude API", "Content-Type": "application/x-x509-user-cert"}) | ||||||
| @@ -133,8 +128,8 @@ def revoke_certificate(common_name): | |||||||
|     push.publish("certificate-revoked", cert.common_name) |     push.publish("certificate-revoked", cert.common_name) | ||||||
|  |  | ||||||
|     # Publish CRL for long polls |     # Publish CRL for long polls | ||||||
|     if config.PUSH_PUBLISH: |     if config.LONG_POLL_PUBLISH: | ||||||
|         url = config.PUSH_PUBLISH % "crl" |         url = config.LONG_POLL_PUBLISH % "crl" | ||||||
|         click.echo("Publishing CRL at %s ..." % url) |         click.echo("Publishing CRL at %s ..." % url) | ||||||
|         requests.post(url, data=export_crl(), |         requests.post(url, data=export_crl(), | ||||||
|             headers={"User-Agent": "Certidude API", "Content-Type": "application/x-pem-file"}) |             headers={"User-Agent": "Certidude API", "Content-Type": "application/x-pem-file"}) | ||||||
| @@ -190,7 +185,7 @@ def delete_request(common_name): | |||||||
|     push.publish("request-deleted", request.common_name) |     push.publish("request-deleted", request.common_name) | ||||||
|  |  | ||||||
|     # Write empty certificate to long-polling URL |     # Write empty certificate to long-polling URL | ||||||
|     requests.delete(config.PUSH_PUBLISH % request.fingerprint(), |     requests.delete(config.LONG_POLL_PUBLISH % request.fingerprint(), | ||||||
|         headers={"User-Agent": "Certidude API"}) |         headers={"User-Agent": "Certidude API"}) | ||||||
|  |  | ||||||
| def generate_ovpn_bundle(common_name, owner=None): | def generate_ovpn_bundle(common_name, owner=None): | ||||||
| @@ -206,8 +201,6 @@ def generate_ovpn_bundle(common_name, owner=None): | |||||||
|     csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([ |     csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([ | ||||||
|         x509.NameAttribute(k, v) for k, v in ( |         x509.NameAttribute(k, v) for k, v in ( | ||||||
|             (NameOID.COMMON_NAME, common_name), |             (NameOID.COMMON_NAME, common_name), | ||||||
|             (NameOID.GIVEN_NAME, owner and owner.given_name), |  | ||||||
|             (NameOID.SURNAME, owner and owner.surname), |  | ||||||
|         ) if v |         ) if v | ||||||
|     ])) |     ])) | ||||||
|  |  | ||||||
| @@ -244,8 +237,6 @@ def generate_pkcs12_bundle(common_name, key_size=4096, owner=None): | |||||||
|     csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([ |     csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([ | ||||||
|         x509.NameAttribute(k, v) for k, v in ( |         x509.NameAttribute(k, v) for k, v in ( | ||||||
|             (NameOID.COMMON_NAME, common_name), |             (NameOID.COMMON_NAME, common_name), | ||||||
|             (NameOID.GIVEN_NAME, owner and owner.given_name), |  | ||||||
|             (NameOID.SURNAME, owner and owner.surname), |  | ||||||
|         ) if v |         ) if v | ||||||
|     ])) |     ])) | ||||||
|  |  | ||||||
| @@ -262,7 +253,12 @@ def generate_pkcs12_bundle(common_name, key_size=4096, owner=None): | |||||||
|         csr.sign(key, hashes.SHA512(), default_backend()).public_bytes(serialization.Encoding.PEM)), overwrite=True) |         csr.sign(key, hashes.SHA512(), default_backend()).public_bytes(serialization.Encoding.PEM)), overwrite=True) | ||||||
|  |  | ||||||
|     # Generate P12, currently supported only by PyOpenSSL |     # Generate P12, currently supported only by PyOpenSSL | ||||||
|  |     try: | ||||||
|         from OpenSSL import crypto |         from OpenSSL import crypto | ||||||
|  |     except ImportError: | ||||||
|  |         logger.error("For P12 bundles please install pyOpenSSL: pip install pyOpenSSL") | ||||||
|  |         raise | ||||||
|  |     else: | ||||||
|         p12 = crypto.PKCS12() |         p12 = crypto.PKCS12() | ||||||
|         p12.set_privatekey( |         p12.set_privatekey( | ||||||
|             crypto.load_privatekey( |             crypto.load_privatekey( | ||||||
|   | |||||||
| @@ -305,8 +305,6 @@ def certidude_request(fork): | |||||||
| @click.command("client", help="Setup X.509 certificates for application") | @click.command("client", help="Setup X.509 certificates for application") | ||||||
| @click.argument("server") | @click.argument("server") | ||||||
| @click.option("--common-name", "-cn", default=const.HOSTNAME, help="Common name, '%s' by default" % const.HOSTNAME) | @click.option("--common-name", "-cn", default=const.HOSTNAME, help="Common name, '%s' by default" % const.HOSTNAME) | ||||||
| @click.option("--org-unit", "-ou", help="Organizational unit") |  | ||||||
| @click.option("--email-address", "-m", default=EMAIL, help="E-mail associated with the request, '%s' by default" % EMAIL) |  | ||||||
| @click.option("--given-name", "-gn", default=FIRST_NAME, help="Given name of the person associted with the certificate, '%s' by default" % FIRST_NAME) | @click.option("--given-name", "-gn", default=FIRST_NAME, help="Given name of the person associted with the certificate, '%s' by default" % FIRST_NAME) | ||||||
| @click.option("--surname", "-sn", default=SURNAME, help="Surname of the person associted with the certificate, '%s' by default" % SURNAME) | @click.option("--surname", "-sn", default=SURNAME, help="Surname of the person associted with the certificate, '%s' by default" % SURNAME) | ||||||
| @click.option("--key-usage", "-ku", help="Key usage attributes, none requested by default") | @click.option("--key-usage", "-ku", help="Key usage attributes, none requested by default") | ||||||
| @@ -325,8 +323,6 @@ def certidude_setup_client(quiet, **kwargs): | |||||||
|  |  | ||||||
| @click.command("server", help="Set up OpenVPN server") | @click.command("server", help="Set up OpenVPN server") | ||||||
| @click.argument("authority") | @click.argument("authority") | ||||||
| @click.option("--org-unit", "-ou", help="Organizational unit") |  | ||||||
| @click.option("--email-address", "-m", default=EMAIL, help="E-mail associated with the request, '%s' by default" % EMAIL) |  | ||||||
| @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") | ||||||
| @@ -336,7 +332,7 @@ def certidude_setup_client(quiet, **kwargs): | |||||||
|     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, email_address, org_unit, local, proto, port): | def certidude_setup_openvpn_server(authority, config, subnet, route, org_unit, local, proto, port): | ||||||
|  |  | ||||||
|     # TODO: Make dirs |     # TODO: Make dirs | ||||||
|     # TODO: Intelligent way of getting last IP address in the subnet |     # TODO: Intelligent way of getting last IP address in the subnet | ||||||
| @@ -428,7 +424,6 @@ def certidude_setup_openvpn_server(authority, config, subnet, route, email_addre | |||||||
| @click.command("nginx", help="Set up nginx as HTTPS server") | @click.command("nginx", help="Set up nginx as HTTPS server") | ||||||
| @click.argument("server") | @click.argument("server") | ||||||
| @click.option("--common-name", "-cn", default=const.FQDN, help="Common name, %s by default" % const.FQDN) | @click.option("--common-name", "-cn", default=const.FQDN, help="Common name, %s by default" % const.FQDN) | ||||||
| @click.option("--org-unit", "-ou", help="Organizational unit") |  | ||||||
| @click.option("--tls-config", | @click.option("--tls-config", | ||||||
|     default="/etc/nginx/conf.d/tls.conf", |     default="/etc/nginx/conf.d/tls.conf", | ||||||
|     type=click.File(mode="w", atomic=True, lazy=True), |     type=click.File(mode="w", atomic=True, lazy=True), | ||||||
| @@ -496,7 +491,6 @@ def certidude_setup_nginx(authority, site_config, tls_config, common_name, org_u | |||||||
| @click.argument("authority") | @click.argument("authority") | ||||||
| @click.argument("remote") | @click.argument("remote") | ||||||
| @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("--org-unit", "-ou", help="Organizational unit") |  | ||||||
| @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), | ||||||
| @@ -568,12 +562,10 @@ def certidude_setup_openvpn_client(authority, remote, config, org_unit, proto): | |||||||
|  |  | ||||||
| @click.command("server", help="Set up strongSwan server") | @click.command("server", help="Set up strongSwan server") | ||||||
| @click.argument("server") | @click.argument("server") | ||||||
| @click.option("--org-unit", "-ou", help="Organizational unit") |  | ||||||
| @click.option("--email-address", "-m", default=EMAIL, help="E-mail associated with the request, %s by default" % EMAIL) |  | ||||||
| @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("--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("--local", "-l", type=ip_address, help="IP address associated with the certificate, none by default") | @click.option("--local", "-l", type=ip_address, help="IP address associated with the certificate, none by default") | ||||||
| @click.option("--route", "-r", type=ip_network, multiple=True, help="Subnets to advertise via this connection, multiple allowed") | @click.option("--route", "-r", type=ip_network, multiple=True, help="Subnets to advertise via this connection, multiple allowed") | ||||||
| def certidude_setup_strongswan_server(authority, config, secrets, subnet, route, email_address, org_unit, local, fqdn): | def certidude_setup_strongswan_server(authority, config, secrets, subnet, route, local, fqdn): | ||||||
|     if "." not in common_name: |     if "." not in common_name: | ||||||
|         raise ValueError("Hostname has to be fully qualified!") |         raise ValueError("Hostname has to be fully qualified!") | ||||||
|     if not local: |     if not local: | ||||||
| @@ -627,7 +619,6 @@ def certidude_setup_strongswan_server(authority, config, secrets, subnet, route, | |||||||
| @click.command("client", help="Set up strongSwan client") | @click.command("client", help="Set up strongSwan client") | ||||||
| @click.argument("server") | @click.argument("server") | ||||||
| @click.argument("remote") | @click.argument("remote") | ||||||
| @click.option("--org-unit", "-ou", help="Organizational unit") |  | ||||||
| def certidude_setup_strongswan_client(authority, config, org_unit, remote, dpdaction): | def certidude_setup_strongswan_client(authority, config, org_unit, remote, dpdaction): | ||||||
|     # Create corresponding section in /etc/certidude/client.conf |     # Create corresponding section in /etc/certidude/client.conf | ||||||
|     client_config = ConfigParser() |     client_config = ConfigParser() | ||||||
| @@ -675,7 +666,6 @@ def certidude_setup_strongswan_client(authority, config, org_unit, remote, dpdac | |||||||
| @click.command("networkmanager", help="Set up strongSwan client via NetworkManager") | @click.command("networkmanager", help="Set up strongSwan client via NetworkManager") | ||||||
| @click.argument("server") # Certidude server | @click.argument("server") # Certidude server | ||||||
| @click.argument("remote") # StrongSwan gateway | @click.argument("remote") # StrongSwan gateway | ||||||
| @click.option("--org-unit", "-ou", help="Organizational unit") |  | ||||||
| def certidude_setup_strongswan_networkmanager(server,remote,  org_unit): | def certidude_setup_strongswan_networkmanager(server,remote,  org_unit): | ||||||
|     endpoint = "IPSec to %s" % remote |     endpoint = "IPSec to %s" % remote | ||||||
|  |  | ||||||
| @@ -721,9 +711,7 @@ def certidude_setup_strongswan_networkmanager(server,remote,  org_unit): | |||||||
| @click.argument("server") # Certidude server | @click.argument("server") # Certidude server | ||||||
| @click.argument("remote") # OpenVPN gateway | @click.argument("remote") # OpenVPN gateway | ||||||
| @click.option("--common-name", "-cn", default=const.HOSTNAME, help="Common name, %s by default" % const.HOSTNAME) | @click.option("--common-name", "-cn", default=const.HOSTNAME, help="Common name, %s by default" % const.HOSTNAME) | ||||||
| @click.option("--org-unit", "-ou", help="Organizational unit") | def certidude_setup_openvpn_networkmanager(authority, org_unit, remote): | ||||||
| @click.option("--email-address", "-m", help="E-mail associated with the request, none by default") |  | ||||||
| def certidude_setup_openvpn_networkmanager(authority, email_address, org_unit, remote): |  | ||||||
|     # Create corresponding section in /etc/certidude/client.conf |     # Create corresponding section in /etc/certidude/client.conf | ||||||
|     client_config = ConfigParser() |     client_config = ConfigParser() | ||||||
|     if os.path.exists(const.CLIENT_CONFIG_PATH): |     if os.path.exists(const.CLIENT_CONFIG_PATH): | ||||||
| @@ -781,11 +769,10 @@ def certidude_setup_openvpn_networkmanager(authority, email_address, org_unit, r | |||||||
| @click.option("--revoked-url", default=None, help="CRL distribution URL") | @click.option("--revoked-url", default=None, help="CRL distribution URL") | ||||||
| @click.option("--certificate-url", default=None, help="Authority certificate URL") | @click.option("--certificate-url", default=None, help="Authority certificate URL") | ||||||
| @click.option("--push-server", default="http://" + const.FQDN, help="Push server, by default http://%s" % const.FQDN) | @click.option("--push-server", default="http://" + const.FQDN, help="Push server, by default http://%s" % const.FQDN) | ||||||
| @click.option("--email-address", default="certidude@" + const.FQDN, help="E-mail address of the CA") |  | ||||||
| @click.option("--directory", help="Directory for authority files") | @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("--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, static_path, kerberos_keytab, nginx_config, parent, country, state, locality, organization, organizational_unit, common_name, directory, certificate_lifetime, authority_lifetime, revocation_list_lifetime, revoked_url, certificate_url, push_server, email_address, outbox, server_flags): | def certidude_setup_authority(username, static_path, kerberos_keytab, nginx_config, parent, country, state, locality, organization, organizational_unit, common_name, directory, certificate_lifetime, authority_lifetime, revocation_list_lifetime, revoked_url, certificate_url, push_server, outbox, server_flags): | ||||||
|  |  | ||||||
|     if not directory: |     if not directory: | ||||||
|         if os.getuid(): |         if os.getuid(): | ||||||
| @@ -833,6 +820,7 @@ def certidude_setup_authority(username, static_path, kerberos_keytab, nginx_conf | |||||||
|             name = cp.get("global", "netbios name") |             name = cp.get("global", "netbios name") | ||||||
|  |  | ||||||
|             base = ",".join(["dc=" + j for j in domain.split(".")]) |             base = ",".join(["dc=" + j for j in domain.split(".")]) | ||||||
|  |             if not os.path.exists("/etc/cron.hourly/certidude"): | ||||||
|                 with open("/etc/cron.hourly/certidude", "w") as fh: |                 with open("/etc/cron.hourly/certidude", "w") as fh: | ||||||
|                     fh.write(env.get_template("ldap-ticket-renewal.sh").render(vars())) |                     fh.write(env.get_template("ldap-ticket-renewal.sh").render(vars())) | ||||||
|                 os.chmod("/etc/cron.hourly/certidude", 0o755) |                 os.chmod("/etc/cron.hourly/certidude", 0o755) | ||||||
| @@ -919,10 +907,10 @@ def certidude_setup_authority(username, static_path, kerberos_keytab, nginx_conf | |||||||
|             ).not_valid_after( |             ).not_valid_after( | ||||||
|                 datetime.utcnow() + timedelta(days=authority_lifetime) |                 datetime.utcnow() + timedelta(days=authority_lifetime) | ||||||
|             ).serial_number(1 |             ).serial_number(1 | ||||||
|             ).add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True, |             ).add_extension(x509.BasicConstraints(ca=True, path_length=0), critical=True, | ||||||
|             ).add_extension(x509.KeyUsage( |             ).add_extension(x509.KeyUsage( | ||||||
|                 digital_signature=True, |                 digital_signature=server_flags, | ||||||
|                 key_encipherment=False, |                 key_encipherment=server_flags, | ||||||
|                 content_commitment=False, |                 content_commitment=False, | ||||||
|                 data_encipherment=False, |                 data_encipherment=False, | ||||||
|                 key_agreement=False, |                 key_agreement=False, | ||||||
| @@ -930,12 +918,6 @@ def certidude_setup_authority(username, static_path, kerberos_keytab, nginx_conf | |||||||
|                 crl_sign=True, |                 crl_sign=True, | ||||||
|                 encipher_only=False, |                 encipher_only=False, | ||||||
|                 decipher_only=False), critical=True, |                 decipher_only=False), critical=True, | ||||||
|             ).add_extension( |  | ||||||
|                 x509.SubjectAlternativeName([ |  | ||||||
|                     x509.DNSName(common_name), |  | ||||||
|                     x509.RFC822Name(email_address) |  | ||||||
|                 ]), |  | ||||||
|                 critical=False, |  | ||||||
|             ).add_extension( |             ).add_extension( | ||||||
|                 x509.SubjectKeyIdentifier.from_public_key(key.public_key()), |                 x509.SubjectKeyIdentifier.from_public_key(key.public_key()), | ||||||
|                 critical=False |                 critical=False | ||||||
| @@ -1132,35 +1114,8 @@ def certidude_list(verbose, show_key_type, show_extensions, show_path, show_sign | |||||||
| def certidude_sign(common_name, overwrite, lifetime): | def certidude_sign(common_name, overwrite, lifetime): | ||||||
|     from certidude import authority, config |     from certidude import authority, config | ||||||
|     request = authority.get_request(common_name) |     request = authority.get_request(common_name) | ||||||
|  |  | ||||||
|     # Use signer if this is regular client CSR |  | ||||||
|     if request.signable: |  | ||||||
|         # Sign via signer process |  | ||||||
|     cert = authority.sign(request) |     cert = authority.sign(request) | ||||||
|  |  | ||||||
|     # Sign directly if it's eg. TLS server CSR |  | ||||||
|     else: |  | ||||||
|         # Load CA private key and certificate |  | ||||||
|         private_key = serialization.load_pem_private_key( |  | ||||||
|             open(config.AUTHORITY_PRIVATE_KEY_PATH).read(), |  | ||||||
|             password=None, # TODO: Ask password for private key? |  | ||||||
|             backend=default_backend()) |  | ||||||
|         authority_certificate = x509.load_pem_x509_certificate( |  | ||||||
|             open(config.AUTHORITY_CERTIFICATE_PATH).read(), |  | ||||||
|             backend=default_backend()) |  | ||||||
|  |  | ||||||
|         # Drop privileges |  | ||||||
|         # to use LDAP service ticket to read usernames of the admins group |  | ||||||
|         # in order to send e-mail |  | ||||||
|         _, _, uid, gid, gecos, root, shell = pwd.getpwnam("certidude") |  | ||||||
|         os.setgroups([]) |  | ||||||
|         os.setgid(gid) |  | ||||||
|         os.setuid(uid) |  | ||||||
|  |  | ||||||
|         # Sign directly using private key |  | ||||||
|         cert = authority.sign2(request, private_key, authority_certificate, |  | ||||||
|             overwrite, True, lifetime) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @click.command("serve", help="Run server") | @click.command("serve", help="Run server") | ||||||
| @click.option("-p", "--port", default=8080 if os.getuid() else 80, help="Listen port") | @click.option("-p", "--port", default=8080 if os.getuid() else 80, help="Listen port") | ||||||
| @@ -1292,9 +1247,9 @@ def certidude_serve(port, listen): | |||||||
|     elif config.LOGGING_BACKEND: |     elif config.LOGGING_BACKEND: | ||||||
|         raise ValueError("Invalid logging.backend = %s" % config.LOGGING_BACKEND) |         raise ValueError("Invalid logging.backend = %s" % config.LOGGING_BACKEND) | ||||||
|  |  | ||||||
|     if config.PUSH_PUBLISH: |     if config.EVENT_SOURCE_PUBLISH: | ||||||
|         from certidude.push import PushLogHandler |         from certidude.push import EventSourceLogHandler | ||||||
|         log_handlers.append(PushLogHandler()) |         log_handlers.append(EventSourceLogHandler()) | ||||||
|  |  | ||||||
|     for facility in "api", "cli": |     for facility in "api", "cli": | ||||||
|         logger = logging.getLogger(facility) |         logger = logging.getLogger(facility) | ||||||
| @@ -1310,7 +1265,6 @@ def certidude_serve(port, listen): | |||||||
|     atexit.register(exit_handler) |     atexit.register(exit_handler) | ||||||
|  |  | ||||||
|     logging.getLogger("cli").debug("Started Certidude at %s", const.FQDN) |     logging.getLogger("cli").debug("Started Certidude at %s", const.FQDN) | ||||||
|     print "Ready" |  | ||||||
|     httpd.serve_forever() |     httpd.serve_forever() | ||||||
|  |  | ||||||
| @click.group("strongswan", help="strongSwan helpers") | @click.group("strongswan", help="strongSwan helpers") | ||||||
|   | |||||||
| @@ -38,7 +38,10 @@ AUTHORITY_CERTIFICATE_PATH = cp.get("authority", "certificate path") | |||||||
| REQUESTS_DIR = cp.get("authority", "requests dir") | REQUESTS_DIR = cp.get("authority", "requests dir") | ||||||
| SIGNED_DIR = cp.get("authority", "signed dir") | SIGNED_DIR = cp.get("authority", "signed dir") | ||||||
| REVOKED_DIR = cp.get("authority", "revoked dir") | REVOKED_DIR = cp.get("authority", "revoked dir") | ||||||
| OUTBOX = cp.get("authority", "outbox") |  | ||||||
|  | OUTBOX = cp.get("authority", "outbox uri") | ||||||
|  | OUTBOX_NAME = cp.get("authority", "outbox sender name") | ||||||
|  | OUTBOX_MAIL = cp.get("authority", "outbox sender address") | ||||||
|  |  | ||||||
| BUNDLE_FORMAT = cp.get("authority", "bundle format") | BUNDLE_FORMAT = cp.get("authority", "bundle format") | ||||||
| OPENVPN_BUNDLE_TEMPLATE = cp.get("authority", "openvpn bundle template") | OPENVPN_BUNDLE_TEMPLATE = cp.get("authority", "openvpn bundle template") | ||||||
| @@ -59,10 +62,11 @@ CERTIFICATE_CRL_URL = cp.get("signature", "revoked url") | |||||||
|  |  | ||||||
| REVOCATION_LIST_LIFETIME = cp.getint("signature", "revocation list lifetime") | REVOCATION_LIST_LIFETIME = cp.getint("signature", "revocation list lifetime") | ||||||
|  |  | ||||||
| PUSH_TOKEN = cp.get("push", "token") | EVENT_SOURCE_TOKEN = cp.get("push", "event source token") | ||||||
| PUSH_EVENT_SOURCE = cp.get("push", "event source") | EVENT_SOURCE_PUBLISH = cp.get("push", "event source publish") | ||||||
| PUSH_LONG_POLL = cp.get("push", "long poll") | EVENT_SOURCE_SUBSCRIBE = cp.get("push", "event source subscribe") | ||||||
| PUSH_PUBLISH = cp.get("push", "publish") | LONG_POLL_PUBLISH = cp.get("push", "long poll publish") | ||||||
|  | LONG_POLL_SUBSCRIBE = cp.get("push", "long poll subscribe") | ||||||
|  |  | ||||||
| TAGGING_BACKEND = cp.get("tagging", "backend") | TAGGING_BACKEND = cp.get("tagging", "backend") | ||||||
| LOGGING_BACKEND = cp.get("logging", "backend") | LOGGING_BACKEND = cp.get("logging", "backend") | ||||||
|   | |||||||
| @@ -52,12 +52,12 @@ def event_source(func): | |||||||
|     return wrapped |     return wrapped | ||||||
|  |  | ||||||
| class MyEncoder(json.JSONEncoder): | class MyEncoder(json.JSONEncoder): | ||||||
|     REQUEST_ATTRIBUTES = "signable", "identity", "changed", "common_name", \ |     REQUEST_ATTRIBUTES = "is_client", "identity", "changed", "common_name", \ | ||||||
|         "organizational_unit", "given_name", "surname", "fqdn", "email_address", \ |         "organizational_unit", "fqdn", \ | ||||||
|         "key_type", "key_length", "md5sum", "sha1sum", "sha256sum", "key_usage" |         "key_type", "key_length", "md5sum", "sha1sum", "sha256sum", "key_usage" | ||||||
|  |  | ||||||
|     CERTIFICATE_ATTRIBUTES = "revokable", "identity", "common_name", \ |     CERTIFICATE_ATTRIBUTES = "revokable", "identity", "common_name", \ | ||||||
|         "organizational_unit", "given_name", "surname", "fqdn", "email_address", \ |         "organizational_unit", "fqdn", \ | ||||||
|         "key_type", "key_length", "sha256sum", "serial_number", "key_usage", \ |         "key_type", "key_length", "sha256sum", "serial_number", "key_usage", \ | ||||||
|         "signed", "expires" |         "signed", "expires" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID, AuthorityInforma | |||||||
| from configparser import ConfigParser | from configparser import ConfigParser | ||||||
| from OpenSSL import crypto | from OpenSSL import crypto | ||||||
|  |  | ||||||
| def certidude_request_certificate(server, key_path, request_path, certificate_path, authority_path, revocations_path, common_name, extended_key_usage_flags=None, org_unit=None, email_address=None, given_name=None, surname=None, autosign=False, wait=False, ip_address=None, dns=None, bundle=False, insecure=False): | def certidude_request_certificate(server, key_path, request_path, certificate_path, authority_path, revocations_path, common_name, autosign=False, wait=False, ip_address=None, bundle=False, insecure=False): | ||||||
|     """ |     """ | ||||||
|     Exchange CSR for certificate using Certidude HTTP API server |     Exchange CSR for certificate using Certidude HTTP API server | ||||||
|     """ |     """ | ||||||
| @@ -127,40 +127,11 @@ def certidude_request_certificate(server, key_path, request_path, certificate_pa | |||||||
|  |  | ||||||
|         # Set subject name attributes |         # Set subject name attributes | ||||||
|         names = [x509.NameAttribute(NameOID.COMMON_NAME, common_name.decode("utf-8"))] |         names = [x509.NameAttribute(NameOID.COMMON_NAME, common_name.decode("utf-8"))] | ||||||
|         if given_name: |  | ||||||
|             names.append(x509.NameAttribute(NameOID.GIVEN_NAME, given_name.decode("utf-8"))) |  | ||||||
|         if surname: |  | ||||||
|             names.append(x509.NameAttribute(NameOID.SURNAME, surname.decode("utf-8"))) |  | ||||||
|         if org_unit: |  | ||||||
|             names.append(x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT, org_unit.decode("utf-8"))) |  | ||||||
|  |  | ||||||
|         # Collect subject alternative names |  | ||||||
|         subject_alt_names = set() |  | ||||||
|         if email_address: |  | ||||||
|             subject_alt_names.add(x509.RFC822Name(email_address)) |  | ||||||
|         if ip_address: |  | ||||||
|             subject_alt_names.add("IP:%s" % ip_address) |  | ||||||
|         if dns: |  | ||||||
|             subject_alt_names.add(x509.DNSName(dns)) |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         # Construct CSR |         # Construct CSR | ||||||
|         csr = x509.CertificateSigningRequestBuilder( |         csr = x509.CertificateSigningRequestBuilder( | ||||||
|             ).subject_name(x509.Name(names)) |             ).subject_name(x509.Name(names)) | ||||||
|  |  | ||||||
|  |  | ||||||
|         if extended_key_usage_flags: |  | ||||||
|             click.echo("Adding extended key usage extension: %s" % extended_key_usage_flags) |  | ||||||
|             csr = csr.add_extension(x509.ExtendedKeyUsage( |  | ||||||
|                 extended_key_usage_flags), critical=True) |  | ||||||
|  |  | ||||||
|         if subject_alt_names: |  | ||||||
|             click.echo("Adding subject alternative name extension: %s" % subject_alt_names) |  | ||||||
|             csr = csr.add_extension( |  | ||||||
|                 x509.SubjectAlternativeName(subject_alt_names), |  | ||||||
|                 critical=False) |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         # Sign & dump CSR |         # Sign & dump CSR | ||||||
|         os.umask(0o022) |         os.umask(0o022) | ||||||
|         with open(request_path + ".part", "wb") as f: |         with open(request_path + ".part", "wb") as f: | ||||||
|   | |||||||
| @@ -70,7 +70,7 @@ def send(template, to=None, attachments=(), **context): | |||||||
|  |  | ||||||
|     msg = MIMEMultipart("alternative") |     msg = MIMEMultipart("alternative") | ||||||
|     msg["Subject"] = subject |     msg["Subject"] = subject | ||||||
|     msg["From"] = authority.certificate.email_address |     msg["From"] = "%s <%s>" % (config.OUTBOX_NAME, config.OUTBOX_MAIL) | ||||||
|     msg["To"] = recipients |     msg["To"] = recipients | ||||||
|  |  | ||||||
|     part1 = MIMEText(text, "plain") |     part1 = MIMEText(text, "plain") | ||||||
| @@ -93,4 +93,4 @@ def send(template, to=None, attachments=(), **context): | |||||||
|     if username and password: |     if username and password: | ||||||
|         conn.login(username, password) |         conn.login(username, password) | ||||||
|  |  | ||||||
|     conn.sendmail(authority.certificate.email_address, recipients, msg.as_string()) |     conn.sendmail(config.OUTBOX_MAIL, recipients, msg.as_string()) | ||||||
|   | |||||||
| @@ -9,9 +9,9 @@ from certidude import config | |||||||
|  |  | ||||||
| def publish(event_type, event_data): | def publish(event_type, event_data): | ||||||
|     """ |     """ | ||||||
|     Publish event on push server |     Publish event on nchan EventSource publisher | ||||||
|     """ |     """ | ||||||
|     if not config.PUSH_PUBLISH: |     if not config.EVENT_SOURCE_PUBLISH: | ||||||
|         # Push server disabled |         # Push server disabled | ||||||
|         return |         return | ||||||
|  |  | ||||||
| @@ -19,7 +19,7 @@ def publish(event_type, event_data): | |||||||
|         from certidude.decorators import MyEncoder |         from certidude.decorators import MyEncoder | ||||||
|         event_data = json.dumps(event_data, cls=MyEncoder) |         event_data = json.dumps(event_data, cls=MyEncoder) | ||||||
|  |  | ||||||
|     url = config.PUSH_PUBLISH % config.PUSH_TOKEN |     url = config.EVENT_SOURCE_PUBLISH % config.EVENT_SOURCE_TOKEN | ||||||
|     click.echo("Publishing %s event '%s' on %s" % (event_type, event_data, url)) |     click.echo("Publishing %s event '%s' on %s" % (event_type, event_data, url)) | ||||||
|  |  | ||||||
|     try: |     try: | ||||||
| @@ -38,12 +38,11 @@ def publish(event_type, event_data): | |||||||
|         click.echo("Failed to submit event to push server, connection error") |         click.echo("Failed to submit event to push server, connection error") | ||||||
|  |  | ||||||
|  |  | ||||||
| class PushLogHandler(logging.Handler): | class EventSourceLogHandler(logging.Handler): | ||||||
|     """ |     """ | ||||||
|     To be used with Python log handling framework for publishing log entries |     To be used with Python log handling framework for publishing log entries | ||||||
|     """ |     """ | ||||||
|     def emit(self, record): |     def emit(self, record): | ||||||
|         from certidude.push import publish |  | ||||||
|         publish("log-entry", dict( |         publish("log-entry", dict( | ||||||
|             created = datetime.utcfromtimestamp(record.created), |             created = datetime.utcfromtimestamp(record.created), | ||||||
|             message = record.msg % record.args, |             message = record.msg % record.args, | ||||||
|   | |||||||
| @@ -61,20 +61,50 @@ class SignHandler(asynchat.async_chat): | |||||||
|             NotImplemented # TODO: Implement OCSP |             NotImplemented # TODO: Implement OCSP | ||||||
|  |  | ||||||
|         elif cmd == "sign-request": |         elif cmd == "sign-request": | ||||||
|  |             # Only common name and public key are used from request | ||||||
|             request = x509.load_pem_x509_csr(body, default_backend()) |             request = x509.load_pem_x509_csr(body, default_backend()) | ||||||
|             subject = x509.Name([n for n in request.subject if n.oid in DN_WHITELIST]) |             common_name, = request.subject.get_attributes_for_oid(NameOID.COMMON_NAME) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             #subject = x509.Name([n for n in request.subject if n.oid in DN_WHITELIST]) | ||||||
|  |  | ||||||
|  |             # If common name is a fully qualified name assume it has to be signed | ||||||
|  |             # with server certificate flags | ||||||
|  |             server_flags = "." in common_name.value | ||||||
|  |  | ||||||
|  |             # TODO: For fqdn allow autosign with validation | ||||||
|  |  | ||||||
|  |             extended_key_usage_flags = [] | ||||||
|  |             if server_flags: | ||||||
|  |                 extended_key_usage_flags.append( # IKE intermediate for IPSec | ||||||
|  |                     x509.ObjectIdentifier("1.3.6.1.5.5.8.2.2")) | ||||||
|  |                 extended_key_usage_flags.append( # OpenVPN server | ||||||
|  |                     ExtendedKeyUsageOID.SERVER_AUTH) | ||||||
|  |             else: | ||||||
|  |                 extended_key_usage_flags.append( # OpenVPN client | ||||||
|  |                     ExtendedKeyUsageOID.CLIENT_AUTH) | ||||||
|  |  | ||||||
|             cert = x509.CertificateBuilder( |             cert = x509.CertificateBuilder( | ||||||
|                 ).subject_name(subject |                 ).subject_name( | ||||||
|  |                     x509.Name([common_name]) | ||||||
|                 ).serial_number(random.randint( |                 ).serial_number(random.randint( | ||||||
|                     0x1000000000000000000000000000000000000000, |                     0x1000000000000000000000000000000000000000, | ||||||
|                     0xffffffffffffffffffffffffffffffffffffffff) |                     0xffffffffffffffffffffffffffffffffffffffff) | ||||||
|                 ).issuer_name(self.server.certificate.issuer |                 ).issuer_name( | ||||||
|                 ).public_key(request.public_key() |                     self.server.certificate.issuer | ||||||
|                 ).not_valid_before(now - timedelta(hours=1) |                 ).public_key( | ||||||
|                 ).not_valid_after(now + timedelta(days=config.CERTIFICATE_LIFETIME) |                     request.public_key() | ||||||
|                 ).add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True, |                 ).not_valid_before( | ||||||
|                 ).add_extension(x509.KeyUsage( |                     now - timedelta(hours=1) | ||||||
|  |                 ).not_valid_after( | ||||||
|  |                     now + timedelta(days=config.CERTIFICATE_LIFETIME) | ||||||
|  |                 ).add_extension( | ||||||
|  |                     x509.BasicConstraints( | ||||||
|  |                         ca=False, | ||||||
|  |                         path_length=None), | ||||||
|  |                     critical=True, | ||||||
|  |                 ).add_extension( | ||||||
|  |                     x509.KeyUsage( | ||||||
|                         digital_signature=True, |                         digital_signature=True, | ||||||
|                         key_encipherment=True, |                         key_encipherment=True, | ||||||
|                         content_commitment=False, |                         content_commitment=False, | ||||||
| @@ -83,12 +113,15 @@ class SignHandler(asynchat.async_chat): | |||||||
|                         key_cert_sign=False, |                         key_cert_sign=False, | ||||||
|                         crl_sign=False, |                         crl_sign=False, | ||||||
|                         encipher_only=False, |                         encipher_only=False, | ||||||
|                     decipher_only=False), critical=True, |                         decipher_only=False), | ||||||
|                 ).add_extension(x509.ExtendedKeyUsage( |                     critical=True, | ||||||
|                     [ExtendedKeyUsageOID.CLIENT_AUTH] |  | ||||||
|                 ), critical=True, |  | ||||||
|                 ).add_extension( |                 ).add_extension( | ||||||
|                     x509.SubjectKeyIdentifier.from_public_key(request.public_key()), |                     x509.ExtendedKeyUsage( | ||||||
|  |                         extended_key_usage_flags), | ||||||
|  |                     critical=True, | ||||||
|  |                 ).add_extension( | ||||||
|  |                     x509.SubjectKeyIdentifier.from_public_key( | ||||||
|  |                         request.public_key()), | ||||||
|                     critical=False |                     critical=False | ||||||
|                 ).add_extension( |                 ).add_extension( | ||||||
|                     x509.AuthorityInformationAccess([ |                     x509.AuthorityInformationAccess([ | ||||||
|   | |||||||
| @@ -1,11 +1,7 @@ | |||||||
| <li id="request-{{ request.common_name | replace('@', '--') | replace('.', '-') }}" class="filterable"> | <li id="request-{{ request.common_name | replace('@', '--') | replace('.', '-') }}" class="filterable"> | ||||||
|  |  | ||||||
| <a class="button icon download" href="/api/request/{{request.common_name}}/">Fetch</a> | <a class="button icon download" href="/api/request/{{request.common_name}}/">Fetch</a> | ||||||
| {% if request.signable %} |  | ||||||
| <button class="icon sign" onClick="javascript:$(this).addClass('busy');$.ajax({url:'/api/request/{{request.common_name}}/',type:'patch'});">Sign</button> | <button class="icon sign" onClick="javascript:$(this).addClass('busy');$.ajax({url:'/api/request/{{request.common_name}}/',type:'patch'});">Sign</button> | ||||||
| {% else %} |  | ||||||
| <button title="Please use certidude command-line utility to sign unusual requests" disabled>Sign</button> |  | ||||||
| {% endif %} |  | ||||||
| <button class="icon revoke" onClick="javascript:$(this).addClass('busy');$.ajax({url:'/api/request/{{request.common_name}}/',type:'delete'});">Delete</button> | <button class="icon revoke" onClick="javascript:$(this).addClass('busy');$.ajax({url:'/api/request/{{request.common_name}}/',type:'delete'});">Delete</button> | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -52,19 +52,23 @@ request subnets = 0.0.0.0/0 | |||||||
| autosign subnets = 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 | autosign subnets = 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 | ||||||
|  |  | ||||||
| [logging] | [logging] | ||||||
| backend = sql | backend = | ||||||
|  |  | ||||||
|  | ;backend = sql | ||||||
| database = sqlite://{{ directory }}/db.sqlite | database = sqlite://{{ directory }}/db.sqlite | ||||||
|  |  | ||||||
| [tagging] | [tagging] | ||||||
| backend = sql | backend = | ||||||
|  |  | ||||||
|  | ;backend = sql | ||||||
| database = sqlite://{{ directory }}/db.sqlite | database = sqlite://{{ directory }}/db.sqlite | ||||||
|  |  | ||||||
| [leases] | [leases] | ||||||
| backend = | backend = | ||||||
|  |  | ||||||
| ;backend = sql | ;backend = sql | ||||||
| ;schema = strongswan | schema = strongswan | ||||||
| ;database = sqlite://{{ directory }}/db.sqlite | database = sqlite://{{ directory }}/db.sqlite | ||||||
|  |  | ||||||
| # Following was used on an OpenWrt router | # Following was used on an OpenWrt router | ||||||
| # uci set openvpn.s2c.status=/www/status.log | # uci set openvpn.s2c.status=/www/status.log | ||||||
| @@ -80,10 +84,11 @@ certificate url = {{ certificate_url }} | |||||||
| revoked url = {{ revoked_url }} | revoked url = {{ revoked_url }} | ||||||
|  |  | ||||||
| [push] | [push] | ||||||
| token = {{ push_token }} | event source token = {{ push_token }} | ||||||
| event source = {{ push_server }}/ev/%s | event source subscribe = {{ push_server }}/ev/sub/%s | ||||||
| long poll = {{ push_server }}/lp/%s | event source publish = {{ push_server }}/ev/pub/%s | ||||||
| publish = {{ push_server }}/pub?id=%s | long poll subscribe = {{ push_server }}/lp/sub/%s | ||||||
|  | long poll publish = {{ push_server }}/lp/pub/%s | ||||||
|  |  | ||||||
| [authority] | [authority] | ||||||
| # User certificate enrollment specifies whether logged in users are allowed to | # User certificate enrollment specifies whether logged in users are allowed to | ||||||
| @@ -102,7 +107,10 @@ requests dir = {{ directory }}/requests/ | |||||||
| signed dir = {{ directory }}/signed/ | signed dir = {{ directory }}/signed/ | ||||||
| revoked dir = {{ directory }}/revoked/ | revoked dir = {{ directory }}/revoked/ | ||||||
| expired dir = {{ directory }}/expired/ | expired dir = {{ directory }}/expired/ | ||||||
| outbox = {{ outbox }} |  | ||||||
|  | outbox uri = {{ outbox }} | ||||||
|  | outbox sender name = Certificate management | ||||||
|  | outbox sender address = certificates@example.com | ||||||
|  |  | ||||||
| bundle format = p12 | bundle format = p12 | ||||||
| ;bundle format = ovpn | ;bundle format = ovpn | ||||||
|   | |||||||
| @@ -17,21 +17,28 @@ server { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     {% if not push_server %} |     {% if not push_server %} | ||||||
|     location /pub { |     location ~ "^/lp/pub/(.*)" { | ||||||
|         allow 127.0.0.1; |         allow 127.0.0.1; | ||||||
|         nchan_publisher http; |         nchan_publisher; | ||||||
|         nchan_store_messages off; |         nchan_channel_id $1; | ||||||
|         nchan_channel_id $arg_id; |         nchan_message_buffer_length 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     location ~ "^/lp/(.*)" { |     location ~ "^/ev/pub/(.*)" { | ||||||
|  |         allow 127.0.0.1; | ||||||
|  |         nchan_publisher; | ||||||
|  |         nchan_channel_id $1; | ||||||
|  |         nchan_message_buffer_length 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     location ~ "^/lp/sub/(.*)" { | ||||||
|  |         nchan_channel_id $1; | ||||||
|         nchan_subscriber longpoll; |         nchan_subscriber longpoll; | ||||||
|         nchan_channel_id $1; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     location ~ "^/ev/(.*)" { |     location ~ "^/ev/sub/(.*)" { | ||||||
|         nchan_subscriber eventsource; |  | ||||||
|         nchan_channel_id $1; |         nchan_channel_id $1; | ||||||
|  |         nchan_subscriber eventsource; | ||||||
|     } |     } | ||||||
|     {% endif %} |     {% endif %} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,22 +22,6 @@ class CertificateBase: | |||||||
|     def __repr__(self): |     def __repr__(self): | ||||||
|         return self.buf |         return self.buf | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def given_name(self): |  | ||||||
|         return self.subject.GN |  | ||||||
|  |  | ||||||
|     @given_name.setter |  | ||||||
|     def given_name(self, value): |  | ||||||
|         return setattr(self.subject, "GN", value) |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def surname(self): |  | ||||||
|         return self.subject.SN |  | ||||||
|  |  | ||||||
|     @surname.setter |  | ||||||
|     def surname(self, value): |  | ||||||
|         return setattr(self.subject, "SN", value) |  | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def common_name(self): |     def common_name(self): | ||||||
|         return self.subject.CN |         return self.subject.CN | ||||||
| @@ -46,46 +30,6 @@ class CertificateBase: | |||||||
|     def common_name(self, value): |     def common_name(self, value): | ||||||
|         self.subject.CN = value |         self.subject.CN = value | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def country_code(self): |  | ||||||
|         return getattr(self._obj.get_subject(), "C", None) |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def state_or_county(self): |  | ||||||
|         return getattr(self._obj.get_subject(), "S", None) |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def city(self): |  | ||||||
|         return getattr(self._obj.get_subject(), "L", None) |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def organization(self): |  | ||||||
|         return getattr(self._obj.get_subject(), "O", None) |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def organizational_unit(self): |  | ||||||
|         return getattr(self._obj.get_subject(), "OU", None) |  | ||||||
|  |  | ||||||
|     @country_code.setter |  | ||||||
|     def country_code(self, value): |  | ||||||
|         return setattr(self._obj.get_subject(), "C", value) |  | ||||||
|  |  | ||||||
|     @state_or_county.setter |  | ||||||
|     def state_or_county(self, value): |  | ||||||
|         return setattr(self._obj.get_subject(), "S", value) |  | ||||||
|  |  | ||||||
|     @city.setter |  | ||||||
|     def city(self, value): |  | ||||||
|         return setattr(self._obj.get_subject(), "L", value) |  | ||||||
|  |  | ||||||
|     @organization.setter |  | ||||||
|     def organization(self, value): |  | ||||||
|         return setattr(self._obj.get_subject(), "O", value) |  | ||||||
|  |  | ||||||
|     @organizational_unit.setter |  | ||||||
|     def organizational_unit(self, value): |  | ||||||
|         return setattr(self._obj.get_subject(), "OU", value) |  | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def key_usage(self): |     def key_usage(self): | ||||||
|         def iterate(): |         def iterate(): | ||||||
| @@ -139,13 +83,6 @@ class CertificateBase: | |||||||
|                 critical, |                 critical, | ||||||
|                 value.encode("ascii")) for (key,value,critical) in extensions]) |                 value.encode("ascii")) for (key,value,critical) in extensions]) | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def email_address(self): |  | ||||||
|         for bit in self.subject_alt_name.split(", "): |  | ||||||
|             if bit.startswith("email:"): |  | ||||||
|                 return bit[6:] |  | ||||||
|         return "" |  | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def fqdn(self): |     def fqdn(self): | ||||||
|         for bit in self.subject_alt_name.split(", "): |         for bit in self.subject_alt_name.split(", "): | ||||||
| @@ -153,17 +90,6 @@ class CertificateBase: | |||||||
|                 return bit[4:] |                 return bit[4:] | ||||||
|         return "" |         return "" | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def subject_alt_name(self): |  | ||||||
|         for key, value, data in self.extensions: |  | ||||||
|             if key == "subjectAltName": |  | ||||||
|                 return value |  | ||||||
|         return "" |  | ||||||
|  |  | ||||||
|     @subject_alt_name.setter |  | ||||||
|     def subject_alt_name(self, value): |  | ||||||
|         self.set_extension("subjectAltName", value, False) |  | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def pubkey(self): |     def pubkey(self): | ||||||
|         from Crypto.Util import asn1 |         from Crypto.Util import asn1 | ||||||
| @@ -226,11 +152,12 @@ class Request(CertificateBase): | |||||||
|         assert not self.buf or self.buf == self.dump(), "%s is not %s" % (repr(self.buf), repr(self.dump())) |         assert not self.buf or self.buf == self.dump(), "%s is not %s" % (repr(self.buf), repr(self.dump())) | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def signable(self): |     def is_server(self): | ||||||
|         for key, value, data in self.extensions: |         return "." in self.common_name | ||||||
|             if key not in const.EXTENSION_WHITELIST: |  | ||||||
|                 return False |     @property | ||||||
|         return True |     def is_client(self): | ||||||
|  |         return not self.is_server | ||||||
|  |  | ||||||
|     def dump(self): |     def dump(self): | ||||||
|         return crypto.dump_certificate_request(crypto.FILETYPE_PEM, self._obj).decode("ascii") |         return crypto.dump_certificate_request(crypto.FILETYPE_PEM, self._obj).decode("ascii") | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user