diff --git a/certidude/auth.py b/certidude/auth.py index 498c1a0..5b5d15e 100644 --- a/certidude/auth.py +++ b/certidude/auth.py @@ -1,6 +1,5 @@ import click -import falcon import logging import os import re @@ -22,6 +21,7 @@ if "kerberos" in config.AUTHENTICATION_BACKENDS: def authenticate(optional=False): + import falcon def wrapper(func): def kerberos_authenticate(resource, req, resp, *args, **kwargs): # If LDAP enabled and device is not Kerberos capable fall diff --git a/certidude/cli.py b/certidude/cli.py index 83c93d7..8faf6bb 100755 --- a/certidude/cli.py +++ b/certidude/cli.py @@ -16,6 +16,7 @@ import sys from configparser import ConfigParser, NoOptionError, NoSectionError from certidude.helpers import certidude_request_certificate from certidude.common import expand_paths, ip_address, ip_network +from certidude.decorators import apt, rpm, pip from cryptography import x509 from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID from cryptography.hazmat.backends import default_backend @@ -481,6 +482,8 @@ def certidude_setup_nginx(authority, site_config, tls_config, common_name, direc default="/etc/openvpn/client-to-site.conf", type=click.File(mode="w", atomic=True, lazy=True), help="OpenVPN configuration file") +@apt("openvpn python-requests-kerberos") +@rpm("openvpn python2-requests-kerberos") def certidude_setup_openvpn_client(authority, remote, config, proto): # Create corresponding section in Certidude client configuration file @@ -598,6 +601,9 @@ def certidude_setup_strongswan_server(authority, config, secrets, subnet, route, @click.command("client", help="Set up strongSwan client") @click.argument("authority") @click.argument("remote") +@apt("network-manager-openvpn-gnome python-requests-kerberos") +@rpm("NetworkManager-openvpn-gnome python2-requests-kerberos") +@pip("ipsecparse") def certidude_setup_strongswan_client(authority, config, remote, dpdaction): # Create corresponding section in /etc/certidude/client.conf client_config = ConfigParser() @@ -645,6 +651,8 @@ def certidude_setup_strongswan_client(authority, config, remote, dpdaction): @click.command("networkmanager", help="Set up strongSwan client via NetworkManager") @click.argument("authority") # Certidude server @click.argument("remote") # StrongSwan gateway +@apt("strongswan-nm") +@rpm("NetworkManager-strongswan-gnome") def certidude_setup_strongswan_networkmanager(authority, remote): endpoint = "IPSec to %s" % remote @@ -744,6 +752,8 @@ def certidude_setup_openvpn_networkmanager(authority, remote): @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("--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): openvpn_profile_template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates", "openvpn-client.conf") bootstrap_template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates", "bootstrap.conf") diff --git a/certidude/decorators.py b/certidude/decorators.py index c21f4da..42e65c1 100644 --- a/certidude/decorators.py +++ b/certidude/decorators.py @@ -1,7 +1,9 @@ -import falcon +import click import ipaddress import json import logging +import os +import subprocess import types from datetime import date, time, datetime, timedelta from certidude.auth import User @@ -13,6 +15,7 @@ def csrf_protection(func): """ Protect resource from common CSRF attacks by checking user agent and referrer """ + import falcon def wrapped(self, req, resp, *args, **kwargs): # Assume curl and python-requests are used intentionally if req.user_agent.startswith("curl/") or req.user_agent.startswith("python-requests/"): @@ -40,6 +43,7 @@ def csrf_protection(func): def event_source(func): + import falcon def wrapped(self, req, resp, *args, **kwargs): if req.get_header("Accept") == "text/event-stream": resp.status = falcon.HTTP_SEE_OTHER @@ -72,6 +76,7 @@ def serialize(func): """ Falcon response serialization """ + import falcon def wrapped(instance, req, resp, **kwargs): if not req.client_accepts("application/json"): logger.debug("Client did not accept application/json") @@ -83,3 +88,43 @@ def serialize(func): resp.body = json.dumps(func(instance, req, resp, **kwargs), cls=MyEncoder) 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 diff --git a/certidude/firewall.py b/certidude/firewall.py index 7b4410a..b36df49 100644 --- a/certidude/firewall.py +++ b/certidude/firewall.py @@ -1,5 +1,4 @@ -import falcon import logging logger = logging.getLogger("api") @@ -8,6 +7,8 @@ def whitelist_subnets(subnets): """ Validate source IP address of API call against subnet list """ + import falcon + def wrapper(func): def wrapped(self, req, resp, *args, **kwargs): # Check for administration subnet whitelist @@ -26,6 +27,8 @@ def whitelist_subnets(subnets): return wrapper def whitelist_content_types(*content_types): + import falcon + def wrapper(func): def wrapped(self, req, resp, *args, **kwargs): for content_type in content_types: