mirror of
				https://github.com/laurivosandi/certidude
				synced 2025-10-31 01:19:11 +00:00 
			
		
		
		
	Fix CRL distriution points and add authority information access extensions
This commit is contained in:
		| @@ -777,13 +777,14 @@ def certidude_setup_production(username, hostname, push_server, nginx_config, uw | |||||||
| @click.option("--organization", "-o", default=None, help="Company or organization name") | @click.option("--organization", "-o", default=None, help="Company or organization name") | ||||||
| @click.option("--organizational-unit", "-ou", default=None) | @click.option("--organizational-unit", "-ou", default=None) | ||||||
| @click.option("--pkcs11", default=False, is_flag=True, help="Use PKCS#11 token instead of files") | @click.option("--pkcs11", default=False, is_flag=True, help="Use PKCS#11 token instead of files") | ||||||
| @click.option("--crl-distribution-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("--ocsp-responder-url", default=None, help="OCSP responder URL") | @click.option("--ocsp-responder-url", default=None, help="OCSP responder URL") | ||||||
| @click.option("--push-server", default="", help="Streaming nginx push server") | @click.option("--push-server", default="http://push.%s" % constants.DOMAIN, help="Push server, http://push.%s by default" % constants.DOMAIN) | ||||||
| @click.option("--email-address", default="certidude@" + FQDN, help="E-mail address of the CA") | @click.option("--email-address", default="certidude@" + FQDN, help="E-mail address of the CA") | ||||||
| @click.option("--directory", default=os.path.join("/var/lib/certidude", FQDN), help="Directory for authority files, /var/lib/certidude/ by default") | @click.option("--directory", default=os.path.join("/var/lib/certidude", FQDN), help="Directory for authority files, /var/lib/certidude/ by default") | ||||||
| @click.option("--outbox", default="smtp://smtp.%s" % constants.DOMAIN, help="SMTP server, smtp://smtp.%s by default" % constants.DOMAIN) | @click.option("--outbox", default="smtp://smtp.%s" % constants.DOMAIN, help="SMTP server, smtp://smtp.%s by default" % constants.DOMAIN) | ||||||
| def certidude_setup_authority(parent, country, state, locality, organization, organizational_unit, common_name, directory, certificate_lifetime, authority_lifetime, revocation_list_lifetime, pkcs11, crl_distribution_url, ocsp_responder_url, push_server, email_address, outbox): | def certidude_setup_authority(parent, country, state, locality, organization, organizational_unit, common_name, directory, certificate_lifetime, authority_lifetime, revocation_list_lifetime, pkcs11, revoked_url, certificate_url, ocsp_responder_url, push_server, email_address, outbox): | ||||||
|  |  | ||||||
|     # Make sure common_name is valid |     # Make sure common_name is valid | ||||||
|     if not re.match(r"^[\.\-_a-zA-Z0-9]+$", common_name): |     if not re.match(r"^[\.\-_a-zA-Z0-9]+$", common_name): | ||||||
| @@ -806,14 +807,14 @@ def certidude_setup_authority(parent, country, state, locality, organization, or | |||||||
|         key = crypto.PKey() |         key = crypto.PKey() | ||||||
|         key.generate_key(crypto.TYPE_RSA, 4096) |         key.generate_key(crypto.TYPE_RSA, 4096) | ||||||
|  |  | ||||||
|     if not crl_distribution_url: |     if not revoked_url: | ||||||
|         crl_distribution_url = "http://%s/api/revoked/" % common_name |         revoked_url = "http://%s/api/revoked/" % common_name | ||||||
|  |     if not certificate_url: | ||||||
|  |         certificate_url = "http://%s/api/certificate/" % common_name | ||||||
|  |  | ||||||
|     # File paths |     # File paths | ||||||
|     ca_key = os.path.join(directory, "ca_key.pem") |     ca_key = os.path.join(directory, "ca_key.pem") | ||||||
|     ca_crt = os.path.join(directory, "ca_crt.pem") |     ca_crt = os.path.join(directory, "ca_crt.pem") | ||||||
|     ca_crl = os.path.join(directory, "ca_crl.pem") |  | ||||||
|     crl_distribution_points = "URI:%s" % crl_distribution_url |  | ||||||
|  |  | ||||||
|     ca = crypto.X509() |     ca = crypto.X509() | ||||||
|     ca.set_version(2) # This corresponds to X.509v3 |     ca.set_version(2) # This corresponds to X.509v3 | ||||||
| @@ -846,7 +847,7 @@ def certidude_setup_authority(parent, country, state, locality, organization, or | |||||||
|         crypto.X509Extension( |         crypto.X509Extension( | ||||||
|             b"keyUsage", |             b"keyUsage", | ||||||
|             True, |             True, | ||||||
|             b"keyCertSign, cRLSign"), |             b"digitalSignature, keyCertSign, cRLSign"), | ||||||
|         crypto.X509Extension( |         crypto.X509Extension( | ||||||
|             b"extendedKeyUsage", |             b"extendedKeyUsage", | ||||||
|             False, |             False, | ||||||
| @@ -856,10 +857,6 @@ def certidude_setup_authority(parent, country, state, locality, organization, or | |||||||
|             False, |             False, | ||||||
|             b"hash", |             b"hash", | ||||||
|             subject = ca), |             subject = ca), | ||||||
|         crypto.X509Extension( |  | ||||||
|             b"crlDistributionPoints", |  | ||||||
|             False, |  | ||||||
|             crl_distribution_points.encode("ascii")), |  | ||||||
|         crypto.X509Extension( |         crypto.X509Extension( | ||||||
|             b"subjectAltName", |             b"subjectAltName", | ||||||
|             False, |             False, | ||||||
| @@ -905,18 +902,10 @@ def certidude_setup_authority(parent, country, state, locality, organization, or | |||||||
|  |  | ||||||
|     # Create subdirectories with 770 permissions |     # Create subdirectories with 770 permissions | ||||||
|     os.umask(0o007) |     os.umask(0o007) | ||||||
|     for subdir in ("signed", "requests", "revoked"): |     for subdir in ("signed", "requests", "revoked", "expired"): | ||||||
|         if not os.path.exists(os.path.join(directory, subdir)): |         if not os.path.exists(os.path.join(directory, subdir)): | ||||||
|             os.mkdir(os.path.join(directory, subdir)) |             os.mkdir(os.path.join(directory, subdir)) | ||||||
|  |  | ||||||
|     # Create CRL and serial file with 644 permissions |  | ||||||
|     os.umask(0o133) |  | ||||||
|     with open(ca_crl, "wb") as fh: |  | ||||||
|         crl = crypto.CRL() |  | ||||||
|         fh.write(crl.export(ca, key, days=revocation_list_lifetime)) |  | ||||||
|     with open(os.path.join(directory, "serial"), "w") as fh: |  | ||||||
|         fh.write("1") |  | ||||||
|  |  | ||||||
|     # Set permission bits to 640 |     # Set permission bits to 640 | ||||||
|     os.umask(0o137) |     os.umask(0o137) | ||||||
|     with open(certidude_conf, "w") as fh: |     with open(certidude_conf, "w") as fh: | ||||||
| @@ -932,7 +921,6 @@ def certidude_setup_authority(parent, country, state, locality, organization, or | |||||||
|     click.echo() |     click.echo() | ||||||
|     click.echo("Use following commands to inspect the newly created files:") |     click.echo("Use following commands to inspect the newly created files:") | ||||||
|     click.echo() |     click.echo() | ||||||
|     click.echo("  openssl crl -inform PEM -text -noout -in %s | less" % ca_crl) |  | ||||||
|     click.echo("  openssl x509 -text -noout -in %s | less" % ca_crt) |     click.echo("  openssl x509 -text -noout -in %s | less" % ca_crt) | ||||||
|     click.echo("  openssl rsa -check -in %s" % ca_key) |     click.echo("  openssl rsa -check -in %s" % ca_key) | ||||||
|     click.echo("  openssl verify -CAfile %s %s" % (ca_crt, ca_crt)) |     click.echo("  openssl verify -CAfile %s %s" % (ca_crt, ca_crt)) | ||||||
|   | |||||||
| @@ -39,10 +39,13 @@ 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") | ||||||
|  |  | ||||||
|  |  | ||||||
| CERTIFICATE_BASIC_CONSTRAINTS = "CA:FALSE" | CERTIFICATE_BASIC_CONSTRAINTS = "CA:FALSE" | ||||||
| CERTIFICATE_KEY_USAGE_FLAGS = "digitalSignature,keyEncipherment" | CERTIFICATE_KEY_USAGE_FLAGS = "digitalSignature,keyEncipherment" | ||||||
| CERTIFICATE_EXTENDED_KEY_USAGE_FLAGS = "clientAuth" | CERTIFICATE_EXTENDED_KEY_USAGE_FLAGS = "clientAuth" | ||||||
| CERTIFICATE_LIFETIME = int(cp.get("signature", "certificate lifetime")) | CERTIFICATE_LIFETIME = int(cp.get("signature", "certificate lifetime")) | ||||||
|  | CERTIFICATE_AUTHORITY_URL = cp.get("signature", "certificate url") | ||||||
|  | CERTIFICATE_CRL_URL = cp.get("signature", "revoked url") | ||||||
|  |  | ||||||
| REVOCATION_LIST_LIFETIME = int(cp.get("signature", "revocation list lifetime")) | REVOCATION_LIST_LIFETIME = int(cp.get("signature", "revocation list lifetime")) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ from cryptography.hazmat.backends import default_backend | |||||||
| from cryptography.hazmat.primitives import hashes, serialization | from cryptography.hazmat.primitives import hashes, serialization | ||||||
| from cryptography.hazmat.primitives.serialization import Encoding | from cryptography.hazmat.primitives.serialization import Encoding | ||||||
| from datetime import datetime, timedelta | from datetime import datetime, timedelta | ||||||
| from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID | from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID, AuthorityInformationAccessOID | ||||||
| import random | import random | ||||||
|  |  | ||||||
| DN_WHITELIST = NameOID.COMMON_NAME, NameOID.GIVEN_NAME, NameOID.SURNAME, \ | DN_WHITELIST = NameOID.COMMON_NAME, NameOID.GIVEN_NAME, NameOID.SURNAME, \ | ||||||
| @@ -49,7 +49,15 @@ def raw_sign(private_key, ca_cert, request, basic_constraints, lifetime, key_usa | |||||||
|             b"authorityKeyIdentifier", |             b"authorityKeyIdentifier", | ||||||
|             False, |             False, | ||||||
|             b"keyid:always", |             b"keyid:always", | ||||||
|             issuer = ca_cert) |             issuer = ca_cert), | ||||||
|  |         crypto.X509Extension( | ||||||
|  |             b"authorityInfoAccess", | ||||||
|  |             False, | ||||||
|  |             ("caIssuers;URI: %s" % config.CERTIFICATE_AUTHORITY_URL).encode("ascii")), | ||||||
|  |         crypto.X509Extension( | ||||||
|  |             b"crlDistributionPoints", | ||||||
|  |             False, | ||||||
|  |             ("URI: %s" % config.CERTIFICATE_CRL_URL).encode("ascii")) | ||||||
|     ]) |     ]) | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -101,7 +109,7 @@ def raw_sign(private_key, ca_cert, request, basic_constraints, lifetime, key_usa | |||||||
|  |  | ||||||
|     # Generate random serial |     # Generate random serial | ||||||
|     cert.set_serial_number(random.randint(SERIAL_MIN, SERIAL_MAX)) |     cert.set_serial_number(random.randint(SERIAL_MIN, SERIAL_MAX)) | ||||||
|     cert.sign(private_key, 'sha256') |     cert.sign(private_key, 'sha512') | ||||||
|     return cert |     return cert | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -175,6 +183,26 @@ class SignHandler(asynchat.async_chat): | |||||||
|                 ).add_extension( |                 ).add_extension( | ||||||
|                     x509.SubjectKeyIdentifier.from_public_key(request.public_key()), |                     x509.SubjectKeyIdentifier.from_public_key(request.public_key()), | ||||||
|                     critical=False |                     critical=False | ||||||
|  |                 ).add_extension( | ||||||
|  |                     x509.AuthorityInformationAccess([ | ||||||
|  |                         x509.AccessDescription( | ||||||
|  |                             AuthorityInformationAccessOID.CA_ISSUERS, | ||||||
|  |                             x509.UniformResourceIdentifier( | ||||||
|  |                                 config.CERTIFICATE_AUTHORITY_URL) | ||||||
|  |                         ) | ||||||
|  |                     ]), | ||||||
|  |                     critical=False | ||||||
|  |                 ).add_extension( | ||||||
|  |                     x509.CRLDistributionPoints([ | ||||||
|  |                         x509.DistributionPoint( | ||||||
|  |                             full_name=[ | ||||||
|  |                                 x509.UniformResourceIdentifier( | ||||||
|  |                                     config.CERTIFICATE_CRL_URL)], | ||||||
|  |                             relative_name=None, | ||||||
|  |                             crl_issuer=None, | ||||||
|  |                             reasons=None) | ||||||
|  |                     ]), | ||||||
|  |                     critical=False | ||||||
|                 ).add_extension( |                 ).add_extension( | ||||||
|                     x509.AuthorityKeyIdentifier.from_issuer_public_key( |                     x509.AuthorityKeyIdentifier.from_issuer_public_key( | ||||||
|                         self.server.certificate.public_key()), |                         self.server.certificate.public_key()), | ||||||
|   | |||||||
| @@ -1,26 +1,49 @@ | |||||||
| [authentication] | [authentication] | ||||||
|  | # The authentiction backend specifies how the user is authenticated, | ||||||
|  | # in case of 'pam' simplepam.authenticate is used to authenticate against | ||||||
|  | # sshd PAM service. In case of 'kerberos' SPNEGO is used to authenticate | ||||||
|  | # user against eg. Active Directory or Samba4. | ||||||
|  |  | ||||||
| backends = pam | backends = pam | ||||||
| #backends = kerberos | ;backends = kerberos | ||||||
| #backends = ldap | ;backends = ldap | ||||||
| #backends = kerberos ldap | ;backends = kerberos ldap | ||||||
| #backends = kerberos pam | ;backends = kerberos pam | ||||||
|  |  | ||||||
| [accounts] | [accounts] | ||||||
|  | # The accounts backend specifies how the user's given name, surname and e-mail | ||||||
|  | # address are looked up. In case of 'posix' basically 'getent passwd' is performed, | ||||||
|  | # in case of 'ldap' a search is performed on LDAP server specified in /etc/ldap/ldap.conf | ||||||
|  | # with Kerberos credential cache initialized at path specified by 'ldap gssapi credential cache' | ||||||
|  |  | ||||||
| backend = posix | backend = posix | ||||||
| #backend = ldap | ;backend = ldap | ||||||
|  | ldap gssapi credential cache = /run/certidude/krb5cc | ||||||
|  |  | ||||||
| [authorization] | [authorization] | ||||||
|  | # The authorization backend specifies how the users are authorized. | ||||||
|  | # In case of 'posix' simply group membership is asserted, | ||||||
|  | # in case of 'ldap' search filter with username as placeholder is applied. | ||||||
|  |  | ||||||
| backend = posix | backend = posix | ||||||
| #backend = ldap |  | ||||||
| ldap gssapi credential cache = /run/certidude/krb5cc |  | ||||||
| ldap computer filter = (&(objectclass=user)(objectclass=computer)(samaccountname=%s)) |  | ||||||
| ldap user filter = (&(objectclass=user)(objectclass=person)(samaccountname=%s)) |  | ||||||
| ldap admins filter = (&(memberOf=cn=Domain Admins,cn=Users,dc=example,dc=com)(samaccountname=%s)) |  | ||||||
| posix user group = users | posix user group = users | ||||||
| posix admin group = sudo | posix admin group = sudo | ||||||
|  |  | ||||||
|  | ;backend = ldap | ||||||
|  | ldap computer filter = (&(objectclass=user)(objectclass=computer)(samaccountname=%s)) | ||||||
|  | ldap user filter = (&(objectclass=user)(objectclass=person)(samaccountname=%s)) | ||||||
|  | ldap admin filter = (&(memberOf=cn=Domain Admins,cn=Users,dc=example,dc=com)(samaccountname=%s)) | ||||||
|  |  | ||||||
|  | # Users are allowed to log in from user subnets | ||||||
| user subnets = 0.0.0.0/0 | user subnets = 0.0.0.0/0 | ||||||
|  |  | ||||||
|  | # Authority administrators are allowed to sign and revoke certificates from these subnets | ||||||
| admin subnets = 0.0.0.0/0 | admin subnets = 0.0.0.0/0 | ||||||
|  |  | ||||||
|  | # Certificate signing requests are allowed to be submitted from these subnets | ||||||
| request subnets = 0.0.0.0/0 | request subnets = 0.0.0.0/0 | ||||||
|  |  | ||||||
|  | # Certificates are automatically signed for these subnets | ||||||
| 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] | ||||||
| @@ -39,14 +62,18 @@ database = sqlite://{{ directory }}/db.sqlite | |||||||
| [signature] | [signature] | ||||||
| certificate lifetime = 1825 | certificate lifetime = 1825 | ||||||
| revocation list lifetime = 1 | revocation list lifetime = 1 | ||||||
|  | certificate url = {{ certificate_url }} | ||||||
|  | revoked url = {{ revoked_url }} | ||||||
|  |  | ||||||
| [push] | [push] | ||||||
| server = | server = {{ push_server }} | ||||||
|  |  | ||||||
| [authority] | [authority] | ||||||
| private key path = {{ ca_key }} | private key path = {{ ca_key }} | ||||||
| certificate path = {{ ca_crt }} | certificate path = {{ ca_crt }} | ||||||
|  |  | ||||||
| requests dir = {{ directory }}/requests/ | requests dir = {{ directory }}/requests/ | ||||||
| signed dir = {{ directory }}/signed/ | signed dir = {{ directory }}/signed/ | ||||||
| revoked dir = {{ directory }}/revoked/ | revoked dir = {{ directory }}/revoked/ | ||||||
|  | expired dir = {{ directory }}/expired/ | ||||||
| outbox = {{ outbox }} | outbox = {{ outbox }} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user