1
0
mirror of https://github.com/laurivosandi/certidude synced 2024-12-23 00:25:18 +00:00
This commit is contained in:
Lauri Võsandi 2017-04-20 05:20:10 +00:00
parent 772886e4d4
commit 5e812f5194
11 changed files with 92 additions and 97 deletions

View File

@ -29,9 +29,9 @@ class LeaseResource(object):
# TODO: verify signature # TODO: verify signature
common_name = req.get_param("client", required=True) common_name = req.get_param("client", required=True)
path, buf, cert = authority.get_signed(common_name) # TODO: catch exceptions path, buf, cert = authority.get_signed(common_name) # TODO: catch exceptions
if cert.serial != req.get_param_as_int("serial", required=True): # Badum we have OCSP! if cert.serial != req.get_param_as_int("serial"): # OCSP-ish solution for OpenVPN, not exposed for StrongSwan
raise # TODO proper exception raise falcon.HTTPForbidden("Forbidden", "Invalid serial number supplied")
if req.get_param("action") == "client-connect":
xattr.setxattr(path, "user.lease.address", req.get_param("address", required=True).encode("ascii")) xattr.setxattr(path, "user.lease.address", req.get_param("address", required=True).encode("ascii"))
xattr.setxattr(path, "user.lease.last_seen", datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z") xattr.setxattr(path, "user.lease.last_seen", datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z")
push.publish("lease-update", common_name) push.publish("lease-update", common_name)

View File

@ -15,8 +15,7 @@ import subprocess
import sys import sys
from configparser import ConfigParser, NoOptionError, NoSectionError from configparser import ConfigParser, NoOptionError, NoSectionError
from certidude.helpers import certidude_request_certificate from certidude.helpers import certidude_request_certificate
from certidude.common import expand_paths, ip_address, ip_network from certidude.common import expand_paths, ip_address, ip_network, apt, rpm, pip
from certidude.decorators import apt, rpm, pip
from cryptography import x509 from cryptography import x509
from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
@ -39,28 +38,6 @@ env = Environment(loader=PackageLoader("certidude", "templates"), trim_blocks=Tr
NOW = datetime.utcnow().replace(tzinfo=None) NOW = datetime.utcnow().replace(tzinfo=None)
CERTIDUDE_TIMER = """
[Unit]
Description=Run certidude service weekly
[Timer]
OnCalendar=weekly
Persistent=true
Unit=certidude.service
[Install]
WantedBy=timers.target
"""
CERTIDUDE_SERVICE = """
[Unit]
Description=Renew certificates and update revocation lists
[Service]
Type=simple
ExecStart=%s request
"""
@click.command("request", help="Run processes for requesting certificates and configuring services") @click.command("request", help="Run processes for requesting certificates and configuring services")
@click.option("-r", "--renew", default=False, is_flag=True, help="Renew now") @click.option("-r", "--renew", default=False, is_flag=True, help="Renew now")
@click.option("-f", "--fork", default=False, is_flag=True, help="Fork to background") @click.option("-f", "--fork", default=False, is_flag=True, help="Fork to background")
@ -88,15 +65,17 @@ def certidude_request(fork, renew):
if not os.path.exists(run_dir): if not os.path.exists(run_dir):
click.echo("Creating: %s" % run_dir) click.echo("Creating: %s" % run_dir)
os.makedirs(run_dir) os.makedirs(run_dir)
context = globals()
context.update(locals())
if not os.path.exists("/etc/systemd/system/certidude.timer"): if not os.path.exists("/etc/systemd/system/certidude.timer"):
click.echo("Creating systemd timer...") click.echo("Creating systemd timer...")
with open("/etc/systemd/system/certidude.timer", "w") as fh: with open("/etc/systemd/system/certidude.timer", "w") as fh:
fh.write(CERTIDUDE_TIMER) fh.write(env.get_template("client/certidude.timer").render(context))
if not os.path.exists("/etc/systemd/system/certidude.service"): if not os.path.exists("/etc/systemd/system/certidude.service"):
click.echo("Creating systemd service...") click.echo("Creating systemd service...")
with open("/etc/systemd/system/certidude.service", "w") as fh: with open("/etc/systemd/system/certidude.service", "w") as fh:
fh.write(CERTIDUDE_SERVICE % sys.argv[0]) fh.write(env.get_template("client/certidude.service").render(context))
for authority in clients.sections(): for authority in clients.sections():
@ -364,6 +343,9 @@ def certidude_request(fork, renew):
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, local, proto, port): def certidude_setup_openvpn_server(authority, config, subnet, route, local, proto, port):
# Install dependencies
apt("openvpn")
rpm("openvpn")
# Create corresponding section in Certidude client configuration file # Create corresponding section in Certidude client configuration file
client_config = ConfigParser() client_config = ConfigParser()
@ -506,9 +488,10 @@ def certidude_setup_nginx(authority, site_config, tls_config, common_name, direc
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),
help="OpenVPN configuration file") help="OpenVPN configuration file")
@apt("openvpn python-requests-kerberos")
@rpm("openvpn python2-requests-kerberos")
def certidude_setup_openvpn_client(authority, remote, config, proto): def certidude_setup_openvpn_client(authority, remote, config, proto):
# Install dependencies
apt("openvpn")
rpm("openvpn")
# Create corresponding section in Certidude client configuration file # Create corresponding section in Certidude client configuration file
client_config = ConfigParser() client_config = ConfigParser()
@ -531,7 +514,7 @@ def certidude_setup_openvpn_client(authority, remote, config, proto):
click.echo("Section '%s' added to %s" % (authority, const.CLIENT_CONFIG_PATH)) click.echo("Section '%s' added to %s" % (authority, const.CLIENT_CONFIG_PATH))
# Create corresponding section in /etc/certidude/services.conf # Create corresponding section in /etc/certidude/services.conf
endpoint = "OpenVPN connection to %s" % remote endpoint = "OpenVPN to %s" % remote
service_config = ConfigParser() service_config = ConfigParser()
if os.path.exists(const.SERVICES_CONFIG_PATH): if os.path.exists(const.SERVICES_CONFIG_PATH):
service_config.readfp(open(const.SERVICES_CONFIG_PATH)) service_config.readfp(open(const.SERVICES_CONFIG_PATH))
@ -574,13 +557,15 @@ def certidude_setup_openvpn_client(authority, remote, config, proto):
@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("--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("--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")
@apt("strongswan python-requests python-requests-kerberos")
@rpm("strongswan python2-requests python2-requests-kerberos")
@pip("ipsecparse")
def certidude_setup_strongswan_server(authority, common_name, subnet, route): def certidude_setup_strongswan_server(authority, common_name, subnet, route):
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!")
# Install dependencies
apt("strongswan")
rpm("strongswan")
pip("ipsecparse")
# Create corresponding section in Certidude client configuration file # Create corresponding section in Certidude client configuration file
client_config = ConfigParser() client_config = ConfigParser()
if os.path.exists(const.CLIENT_CONFIG_PATH): if os.path.exists(const.CLIENT_CONFIG_PATH):
@ -605,14 +590,10 @@ def certidude_setup_strongswan_server(authority, common_name, subnet, route):
from ipsecparse import loads from ipsecparse import loads
config = loads(open("%s/ipsec.conf" % const.STRONGSWAN_PREFIX).read()) config = loads(open("%s/ipsec.conf" % const.STRONGSWAN_PREFIX).read())
config["conn", authority] = dict( config["conn", authority] = dict(
leftsourceip="%config",
left=common_name,
leftcert=client_config.get(authority, "certificate path"), leftcert=client_config.get(authority, "certificate path"),
leftsubnet=",".join(route), leftsubnet=",".join(route),
right="%any", right="%any",
rightsourceip=str(subnet), rightsourceip=str(subnet),
keyexchange="ikev2",
keyingtries="300",
closeaction="restart", closeaction="restart",
auto="ignore") auto="ignore")
with open("%s/ipsec.conf.part" % const.STRONGSWAN_PREFIX, "w") as fh: with open("%s/ipsec.conf.part" % const.STRONGSWAN_PREFIX, "w") as fh:
@ -629,10 +610,12 @@ def certidude_setup_strongswan_server(authority, common_name, subnet, route):
@click.command("client", help="Set up strongSwan client") @click.command("client", help="Set up strongSwan client")
@click.argument("authority") @click.argument("authority")
@click.argument("remote") @click.argument("remote")
@apt("strongswan python-requests python-requests-kerberos")
@rpm("strongswan python2-requests python2-requests-kerberos")
@pip("ipsecparse")
def certidude_setup_strongswan_client(authority, remote): def certidude_setup_strongswan_client(authority, remote):
# Install dependencies
apt("strongswan")
rpm("strongswan")
pip("ipsecparse")
# 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):
@ -698,9 +681,11 @@ def certidude_setup_strongswan_client(authority, remote):
@click.command("networkmanager", help="Set up strongSwan client via NetworkManager") @click.command("networkmanager", help="Set up strongSwan client via NetworkManager")
@click.argument("authority") # Certidude server @click.argument("authority") # Certidude server
@click.argument("remote") # StrongSwan gateway @click.argument("remote") # StrongSwan gateway
@apt("strongswan-nm python-requests python-requests-kerberos")
@rpm("NetworkManager-strongswan-gnome python2-requests python2-requests-kerberos")
def certidude_setup_strongswan_networkmanager(authority, remote): def certidude_setup_strongswan_networkmanager(authority, remote):
# Install dependencies
apt("strongswan-nm")
rpm("NetworkManager-strongswan-gnome")
endpoint = "IPSec to %s" % remote endpoint = "IPSec to %s" % remote
# Create corresponding section in /etc/certidude/client.conf # Create corresponding section in /etc/certidude/client.conf
@ -799,11 +784,13 @@ def certidude_setup_openvpn_networkmanager(authority, remote):
@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)
@apt("python-setproctitle python-openssl python-falcon python-humanize python-markdown python-xattr")
@rpm("python-setproctitle pyOpenSSL python-falcon python-humanize python-markdown pyxattr")
def certidude_setup_authority(username, kerberos_keytab, nginx_config, country, state, locality, organization, organizational_unit, common_name, directory, authority_lifetime, push_server, outbox, server_flags): def certidude_setup_authority(username, kerberos_keytab, nginx_config, country, state, locality, organization, organizational_unit, common_name, directory, authority_lifetime, push_server, outbox, server_flags):
openvpn_profile_template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates", "openvpn-client.conf") # Install dependencies
bootstrap_template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates", "bootstrap.conf") apt("python-setproctitle python-openssl python-falcon python-humanize python-markdown python-xattr")
rpm("python-setproctitle pyOpenSSL python-falcon python-humanize python-markdown pyxattr")
pip("gssapi")
template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates")
if not directory: if not directory:
if os.getuid(): if os.getuid():
@ -848,7 +835,7 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
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"): 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("server/cronjob").render(vars()))
os.chmod("/etc/cron.hourly/certidude", 0o755) os.chmod("/etc/cron.hourly/certidude", 0o755)
click.echo("Created /etc/cron.hourly/certidude for automatic LDAP service ticket renewal, inspect and adjust accordingly") 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")
@ -865,7 +852,7 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
port = "80" port = "80"
else: else:
port = "8080" port = "8080"
nginx_config.write(env.get_template("nginx.conf").render(vars())) nginx_config.write(env.get_template("server/nginx.conf").render(vars()))
click.echo("Generated: %s" % nginx_config.name) click.echo("Generated: %s" % nginx_config.name)
if not os.path.exists("/etc/nginx/sites-enabled/certidude.conf"): if not os.path.exists("/etc/nginx/sites-enabled/certidude.conf"):
os.symlink("../sites-available/certidude.conf", "/etc/nginx/sites-enabled/certidude.conf") os.symlink("../sites-available/certidude.conf", "/etc/nginx/sites-enabled/certidude.conf")
@ -880,7 +867,7 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
click.echo("File /etc/systemd/system/certidude.service already exists, remove to regenerate") click.echo("File /etc/systemd/system/certidude.service already exists, remove to regenerate")
else: else:
with open("/etc/systemd/system/certidude.service", "w") as fh: with open("/etc/systemd/system/certidude.service", "w") as fh:
fh.write(env.get_template("systemd.service").render(vars())) fh.write(env.get_template("server/systemd.service").render(vars()))
click.echo("File /etc/systemd/system/certidude.service created") click.echo("File /etc/systemd/system/certidude.service created")
else: else:
NotImplemented # No systemd NotImplemented # No systemd
@ -900,7 +887,7 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
os.umask(0o137) os.umask(0o137)
push_token = "".join([random.choice(string.ascii_letters + string.digits) for j in range(0,32)]) push_token = "".join([random.choice(string.ascii_letters + string.digits) for j in range(0,32)])
with open(const.CONFIG_PATH, "w") as fh: with open(const.CONFIG_PATH, "w") as fh:
fh.write(env.get_template("certidude-server.conf").render(vars())) fh.write(env.get_template("server/server.conf").render(vars()))
click.echo("Generated %s" % const.CONFIG_PATH) click.echo("Generated %s" % const.CONFIG_PATH)
if os.path.lexists(directory): if os.path.lexists(directory):

View File

@ -35,3 +35,29 @@ def expand_paths():
return wrapped return wrapped
return wrapper return wrapper
def apt(packages):
"""
Install packages for Debian and Ubuntu
"""
if os.path.exists("/usr/bin/apt-get"):
cmd = ["/usr/bin/apt-get", "install", "-yqq"] + packages.split(" ")
click.echo("Running: %s" % " ".join(cmd))
subprocess.call(cmd)
def rpm(packages):
"""
Install packages for Fedora and CentOS
"""
if os.path.exists("/usr/bin/dnf"):
cmd = ["/usr/bin/dnf", "install", "-y"] + packages.split(" ")
click.echo("Running: %s" % " ".join(cmd))
subprocess.call(cmd)
def pip(packages):
click.echo("Running: pip install %s" % packages)
import pip
pip.main(['install'] + packages.split(" "))

View File

@ -74,6 +74,11 @@ EVENT_SOURCE_SUBSCRIBE = cp.get("push", "event source subscribe")
LONG_POLL_PUBLISH = cp.get("push", "long poll publish") LONG_POLL_PUBLISH = cp.get("push", "long poll publish")
LONG_POLL_SUBSCRIBE = cp.get("push", "long poll subscribe") LONG_POLL_SUBSCRIBE = cp.get("push", "long poll subscribe")
if os.getenv("TRAVIS"): # TODO: include nginx setup in Travis
EVENT_SOURCE_PUBLISH = ""
LONG_POLL_PUBLISH = ""
LONG_POLL_SUBSCRIBE = ""
LOGGING_BACKEND = cp.get("logging", "backend") LOGGING_BACKEND = cp.get("logging", "backend")
if "whitelist" == AUTHORIZATION_BACKEND: if "whitelist" == AUTHORIZATION_BACKEND:

View File

@ -88,43 +88,3 @@ def serialize(func):
resp.body = json.dumps(func(instance, req, resp, **kwargs), cls=MyEncoder) resp.body = json.dumps(func(instance, req, resp, **kwargs), cls=MyEncoder)
return wrapped return wrapped
def apt(packages):
"""
Install packages for Debian and Ubuntu
"""
def wrapper(func):
def wrapped(*args, **kwargs):
if os.path.exists("/usr/bin/apt-get"):
cmd = ["/usr/bin/apt-get", "install", "-yqq"] + packages.split(" ")
click.echo("Running: %s" % " ".join(cmd))
subprocess.call(cmd)
return func(*args, **kwargs)
return wrapped
return wrapper
def rpm(packages):
"""
Install packages for Fedora and CentOS
"""
def wrapper(func):
def wrapped(*args, **kwargs):
if os.path.exists("/usr/bin/dnf"):
cmd = ["/usr/bin/dnf", "install", "-y"] + packages.split(" ")
click.echo("Running: %s" % " ".join(cmd))
subprocess.call(cmd)
return func(*args, **kwargs)
return wrapped
return wrapper
def pip(packages):
def wrapper(func):
def wrapped(*args, **kwargs):
click.echo("Running: pip install %s" % packages)
import pip
pip.main(['install'] + packages.split(" "))
return func(*args, **kwargs)
return wrapped
return wrapper

View File

@ -0,0 +1,6 @@
[Unit]
Description=Renew certificates and update revocation lists
[Service]
Type=simple
ExecStart={{ sys.argv[0] }} request

View File

@ -0,0 +1,11 @@
[Unit]
Description=Run certidude service weekly
[Timer]
OnCalendar=weekly
Persistent=true
Unit=certidude.service
[Install]
WantedBy=timers.target

View File

@ -142,7 +142,7 @@ format = p12
# Template for OpenVPN profile, copy certidude/templates/openvpn-client.conf # Template for OpenVPN profile, copy certidude/templates/openvpn-client.conf
# to /etc/certidude/ and make modifications as necessary # to /etc/certidude/ and make modifications as necessary
openvpn profile template = {{ openvpn_profile_template_path }} openvpn profile template = {{ template_path }}/openvpn-client.conf
[tagging] [tagging]
owner/string = Owner owner/string = Owner
@ -154,4 +154,4 @@ other/ = Other
# Following can be used to set up clients easily: certidude bootstrap ca.example.lan # Following can be used to set up clients easily: certidude bootstrap ca.example.lan
# Services template is rendered on certidude server with relevant variables and # Services template is rendered on certidude server with relevant variables and
# placed to /etc/certidude/services.conf on the client # placed to /etc/certidude/services.conf on the client
services template = {{ bootstrap_template_path }} services template = {{ template_path }}/bootstrap.conf