mirror of
https://github.com/laurivosandi/certidude
synced 2024-12-23 00:25:18 +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:
parent
703970c1d3
commit
2a8109704a
@ -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,11 +52,11 @@ 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()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
# Check if this request has been already signed and return corresponding certificte if it has been signed
|
# Check if this request has been already signed and return corresponding certificte if it has been signed
|
||||||
@ -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,12 +31,7 @@ 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 = None
|
||||||
recipient = "%s %s <%s>" % (cert.given_name, cert.surname, cert.email_address)
|
|
||||||
elif cert.email_address:
|
|
||||||
recipient = cert.email_address
|
|
||||||
else:
|
|
||||||
recipient = None
|
|
||||||
|
|
||||||
mailer.send(
|
mailer.send(
|
||||||
"certificate-signed.md",
|
"certificate-signed.md",
|
||||||
@ -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,21 +253,26 @@ 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
|
||||||
from OpenSSL import crypto
|
try:
|
||||||
p12 = crypto.PKCS12()
|
from OpenSSL import crypto
|
||||||
p12.set_privatekey(
|
except ImportError:
|
||||||
crypto.load_privatekey(
|
logger.error("For P12 bundles please install pyOpenSSL: pip install pyOpenSSL")
|
||||||
crypto.FILETYPE_PEM,
|
raise
|
||||||
key.private_bytes(
|
else:
|
||||||
encoding=serialization.Encoding.PEM,
|
p12 = crypto.PKCS12()
|
||||||
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
p12.set_privatekey(
|
||||||
encryption_algorithm=serialization.NoEncryption()
|
crypto.load_privatekey(
|
||||||
|
crypto.FILETYPE_PEM,
|
||||||
|
key.private_bytes(
|
||||||
|
encoding=serialization.Encoding.PEM,
|
||||||
|
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||||
|
encryption_algorithm=serialization.NoEncryption()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
p12.set_certificate( cert._obj )
|
||||||
p12.set_certificate( cert._obj )
|
p12.set_ca_certificates([certificate._obj])
|
||||||
p12.set_ca_certificates([certificate._obj])
|
return p12.export(), cert
|
||||||
return p12.export(), cert
|
|
||||||
|
|
||||||
|
|
||||||
@publish_certificate
|
@publish_certificate
|
||||||
|
@ -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,10 +820,11 @@ 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(".")])
|
||||||
with open("/etc/cron.hourly/certidude", "w") as fh:
|
if not os.path.exists("/etc/cron.hourly/certidude"):
|
||||||
fh.write(env.get_template("ldap-ticket-renewal.sh").render(vars()))
|
with open("/etc/cron.hourly/certidude", "w") as fh:
|
||||||
os.chmod("/etc/cron.hourly/certidude", 0o755)
|
fh.write(env.get_template("ldap-ticket-renewal.sh").render(vars()))
|
||||||
click.echo("Created /etc/cron.hourly/certidude for automatic LDAP service ticket renewal, inspect and adjust accordingly")
|
os.chmod("/etc/cron.hourly/certidude", 0o755)
|
||||||
|
click.echo("Created /etc/cron.hourly/certidude for automatic LDAP service ticket renewal, inspect and adjust accordingly")
|
||||||
os.system("/etc/cron.hourly/certidude")
|
os.system("/etc/cron.hourly/certidude")
|
||||||
else:
|
else:
|
||||||
click.echo("Warning: /etc/krb5.keytab or /etc/samba/smb.conf not found, Kerberos unconfigured")
|
click.echo("Warning: /etc/krb5.keytab or /etc/samba/smb.conf not found, Kerberos unconfigured")
|
||||||
@ -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,34 +1114,7 @@ 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)
|
||||||
|
cert = authority.sign(request)
|
||||||
# Use signer if this is regular client CSR
|
|
||||||
if request.signable:
|
|
||||||
# Sign via signer process
|
|
||||||
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")
|
||||||
@ -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,34 +61,67 @@ 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)
|
||||||
digital_signature=True,
|
).not_valid_after(
|
||||||
key_encipherment=True,
|
now + timedelta(days=config.CERTIFICATE_LIFETIME)
|
||||||
content_commitment=False,
|
|
||||||
data_encipherment=False,
|
|
||||||
key_agreement=False,
|
|
||||||
key_cert_sign=False,
|
|
||||||
crl_sign=False,
|
|
||||||
encipher_only=False,
|
|
||||||
decipher_only=False), critical=True,
|
|
||||||
).add_extension(x509.ExtendedKeyUsage(
|
|
||||||
[ExtendedKeyUsageOID.CLIENT_AUTH]
|
|
||||||
), critical=True,
|
|
||||||
).add_extension(
|
).add_extension(
|
||||||
x509.SubjectKeyIdentifier.from_public_key(request.public_key()),
|
x509.BasicConstraints(
|
||||||
|
ca=False,
|
||||||
|
path_length=None),
|
||||||
|
critical=True,
|
||||||
|
).add_extension(
|
||||||
|
x509.KeyUsage(
|
||||||
|
digital_signature=True,
|
||||||
|
key_encipherment=True,
|
||||||
|
content_commitment=False,
|
||||||
|
data_encipherment=False,
|
||||||
|
key_agreement=False,
|
||||||
|
key_cert_sign=False,
|
||||||
|
crl_sign=False,
|
||||||
|
encipher_only=False,
|
||||||
|
decipher_only=False),
|
||||||
|
critical=True,
|
||||||
|
).add_extension(
|
||||||
|
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")
|
||||||
|
Loading…
Reference in New Issue
Block a user