From 52d35012a4261ba0fee9bb1d8ce8ea266a600933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauri=20V=C3=B5sandi?= Date: Thu, 13 Apr 2017 20:30:28 +0000 Subject: [PATCH 1/9] Various fixes --- certidude/api/__init__.py | 4 ++-- certidude/api/attrib.py | 3 +-- certidude/authority.py | 5 ++--- certidude/cli.py | 18 ++++++++++-------- certidude/const.py | 7 ++++++- certidude/helpers.py | 2 +- certidude/templates/nginx.conf | 30 +++++++++++++++++++++++++++--- setup.py | 8 -------- 8 files changed, 49 insertions(+), 28 deletions(-) diff --git a/certidude/api/__init__.py b/certidude/api/__init__.py index f04c2d2..65534aa 100644 --- a/certidude/api/__init__.py +++ b/certidude/api/__init__.py @@ -6,7 +6,6 @@ import logging import os import click import hashlib -import xattr from datetime import datetime from time import sleep from certidude import authority, mailer @@ -46,6 +45,8 @@ class SessionResource(object): @login_required @event_source def on_get(self, req, resp): + import xattr + def serialize_requests(g): for common_name, path, buf, obj, server in g(): yield dict( @@ -126,7 +127,6 @@ class SessionResource(object): requests=serialize_requests(authority.list_requests), signed=serialize_certificates(authority.list_signed), revoked=serialize_certificates(authority.list_revoked), - users=User.objects.all(), admin_users = User.objects.filter_admins(), user_subnets = config.USER_SUBNETS, autosign_subnets = config.AUTOSIGN_SUBNETS, diff --git a/certidude/api/attrib.py b/certidude/api/attrib.py index 9186a5d..740f8c6 100644 --- a/certidude/api/attrib.py +++ b/certidude/api/attrib.py @@ -1,11 +1,10 @@ - import falcon import logging from ipaddress import ip_address -from xattr import getxattr, listxattr from datetime import datetime from certidude import config, authority from certidude.decorators import serialize +from xattr import getxattr, listxattr logger = logging.getLogger(__name__) diff --git a/certidude/authority.py b/certidude/authority.py index 73d236a..cbdee51 100644 --- a/certidude/authority.py +++ b/certidude/authority.py @@ -15,7 +15,6 @@ from cryptography.hazmat.primitives import hashes, serialization from certidude import config, push, mailer, const from certidude import errors from jinja2 import Template -from xattr import getxattr, listxattr, setxattr RE_HOSTNAME = "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(@(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9]))?$" @@ -297,6 +296,8 @@ def sign(common_name, overwrite=False): def _sign(csr, buf, overwrite=False): assert buf.startswith("-----BEGIN CERTIFICATE REQUEST-----\n") assert isinstance(csr, x509.CertificateSigningRequest) + from xattr import getxattr, listxattr, setxattr + common_name, = csr.subject.get_attributes_for_oid(NameOID.COMMON_NAME) cert_path = os.path.join(config.SIGNED_DIR, common_name.value + ".pem") renew = False @@ -375,5 +376,3 @@ def _sign(csr, buf, overwrite=False): push.publish("request-signed", common_name.value) return cert, cert_buf - - diff --git a/certidude/cli.py b/certidude/cli.py index 857f9ce..26975a7 100755 --- a/certidude/cli.py +++ b/certidude/cli.py @@ -8,7 +8,6 @@ import os import pwd import random import re -import requests import signal import socket import string @@ -67,6 +66,8 @@ ExecStart=%s request @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") def certidude_request(fork, renew): + import requests + if not os.path.exists(const.CLIENT_CONFIG_PATH): click.echo("No %s!" % const.CLIENT_CONFIG_PATH) return 1 @@ -741,7 +742,7 @@ def certidude_setup_openvpn_networkmanager(authority, remote): @click.option("--authority-lifetime", default=20*365, help="Authority certificate lifetime in days, 20 years by default") @click.option("--organization", "-o", default=None, help="Company or organization name") @click.option("--organizational-unit", "-ou", default=None) -@click.option("--push-server", default="http://" + const.FQDN, help="Push server, by default http://%s" % const.FQDN) +@click.option("--push-server", help="Push server, by default http://%s" % const.FQDN) @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) @@ -799,8 +800,7 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country, else: click.echo("Warning: /etc/krb5.keytab or /etc/samba/smb.conf not found, Kerberos unconfigured") - - working_directory = os.path.realpath(os.path.dirname(__file__)) + static_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), "static") certidude_path = sys.argv[0] # Push server config generation @@ -809,15 +809,16 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country, listen = "0.0.0.0" port = "80" else: - nginx_client_config.write(env.get_template("nginx.conf").render(vars())) - click.echo("Generated: %s" % nginx_client_config.name) + port = "8080" + nginx_config.write(env.get_template("nginx.conf").render(vars())) + click.echo("Generated: %s" % nginx_config.name) if not os.path.exists("/etc/nginx/sites-enabled/certidude.conf"): os.symlink("../sites-available/certidude.conf", "/etc/nginx/sites-enabled/certidude.conf") - click.echo("Symlinked %s -> /etc/nginx/sites-enabled/" % nginx_client_config.name) + click.echo("Symlinked %s -> /etc/nginx/sites-enabled/" % nginx_config.name) if os.path.exists("/etc/nginx/sites-enabled/default"): os.unlink("/etc/nginx/sites-enabled/default") if not push_server: - click.echo("Remember to install nchan instead of regular nginx!") + click.echo("Remember to install nchan capable nginx instead of regular nginx!") if os.path.exists("/etc/systemd"): if os.path.exists("/etc/systemd/system/certidude.service"): @@ -1231,6 +1232,7 @@ def certidude_serve(port, listen, fork): @click.option("-s", "--slot", default="9a", help="Yubikey slot to use, 9a by default") @click.option("-u", "--username", default=os.getenv("USER"), help="Username to use, %s by default" % os.getenv("USER")) def certidude_setup_yubikey(authority, slot, username, pin): + import requests cmd = "ykinfo", "-q", "-s" click.echo("Executing: %s" % " ".join(cmd)) serial = subprocess.check_output(cmd).strip() diff --git a/certidude/const.py b/certidude/const.py index 5d2bb62..22a35b0 100644 --- a/certidude/const.py +++ b/certidude/const.py @@ -2,6 +2,7 @@ import click import os import socket +import sys CONFIG_DIR = os.path.expanduser("~/.certidude") if os.getuid() else "/etc/certidude" CONFIG_PATH = os.path.join(CONFIG_DIR, "server.conf") @@ -18,7 +19,11 @@ SIGNER_LOG_PATH = os.path.join(CONFIG_DIR, "signer.log") if os.getuid() else "/v if os.getenv("TRAVIS"): FQDN = "buildbot" else: - FQDN = socket.getaddrinfo(socket.gethostname(), 0, socket.AF_INET, 0, 0, socket.AI_CANONNAME)[0][3] + try: + FQDN = socket.getaddrinfo(socket.gethostname(), 0, socket.AF_INET, 0, 0, socket.AI_CANONNAME)[0][3] + except socket.gaierror: + click.echo("Failed to resolve fully qualified hostname of this machine, make sure hostname -f works") + sys.exit(254) if "." in FQDN: HOSTNAME, DOMAIN = FQDN.split(".", 1) diff --git a/certidude/helpers.py b/certidude/helpers.py index bcaa15a..fa71392 100644 --- a/certidude/helpers.py +++ b/certidude/helpers.py @@ -1,7 +1,6 @@ import click import os -import requests import subprocess import tempfile from base64 import b64encode @@ -30,6 +29,7 @@ def certidude_request_certificate(server, key_path, request_path, certificate_pa """ Exchange CSR for certificate using Certidude HTTP API server """ + import requests # Create directories for path in key_path, request_path, certificate_path, authority_path, revocations_path: diff --git a/certidude/templates/nginx.conf b/certidude/templates/nginx.conf index 9a52aaa..7c97df1 100644 --- a/certidude/templates/nginx.conf +++ b/certidude/templates/nginx.conf @@ -1,13 +1,29 @@ +# To set up SSL certificates using Let's Encrypt run: +# +# apt install letsencrypt +# certbot certonly -d {{common_name}} --webroot /var/www/html/ +# +# Also uncomment URL rewriting and SSL configuration below server { server_name {{ common_name }}; listen 80 default_server; +# rewrite ^ https://$server_name$request_uri? permanent; +#} + +#server { +# server_name {{ common_name }}; +# listen 443 ssl http2 default_server; +# add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;"; +# ssl_certificate /etc/letsencrypt/live/{{common_name}}/fullchain.pem; +# ssl_certificate_key /etc/letsencrypt/live/{{common_name}}/privkey.pem; + error_page 500 502 503 504 /50x.html; root {{static_path}}; location /api/ { - proxy_pass http://127.0.0.1{% if listen != 80 }:{{ listen }}{% endif %}/api/; + proxy_pass http://127.0.0.1{% if port != 80 %}:{{ port }}{% endif %}/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_connect_timeout 600; @@ -16,7 +32,15 @@ server { send_timeout 600; } - {% if not push_server %} + # This is for Let's Encrypt + location /.well-known/ { + alias /var/www/html/.well-known/; + } + + +{% if not push_server %} + # This only works with nchan, for Debian 9 just apt install libnginx-mod-nchan + # For Ubuntu and older Debian releases install nchan from https://nchan.io/ location ~ "^/lp/pub/(.*)" { allow 127.0.0.1; nchan_publisher; @@ -40,7 +64,7 @@ server { nchan_channel_id $1; nchan_subscriber eventsource; } - {% endif %} +{% endif %} } diff --git a/setup.py b/setup.py index 7a75ae7..4884ee6 100644 --- a/setup.py +++ b/setup.py @@ -18,14 +18,6 @@ setup( ], long_description=open("README.rst").read(), install_requires=[ - "setproctitle", - "click", - "falcon", - "jinja2", - "pyopenssl", - "humanize", - "cryptography", - "markupsafe", ], scripts=[ "misc/certidude" From 02b2f041ccf34cb66f98ffd488bff7efd9f9999b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauri=20V=C3=B5sandi?= Date: Thu, 13 Apr 2017 20:52:09 +0000 Subject: [PATCH 2/9] Clean up dependencies and Travis --- .travis.yml | 9 ++++++++- certidude/cli.py | 5 ++--- requirements.txt | 17 ++++++----------- setup.py | 7 +++++++ 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8bb99f4..b4a9bab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,5 +19,12 @@ cache: addons: apt: packages: - - python-ldap - python-xattr + - python-setproctitle + - python-falcon + - python-markdown + - python-jinja2 + - python-click + - python-configparser + - python-ipaddress + - python-humanize diff --git a/certidude/cli.py b/certidude/cli.py index 26975a7..83c93d7 100755 --- a/certidude/cli.py +++ b/certidude/cli.py @@ -23,9 +23,7 @@ from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa from datetime import datetime, timedelta -from humanize import naturaltime from jinja2 import Environment, PackageLoader -from setproctitle import setproctitle import const logger = logging.getLogger(__name__) @@ -972,7 +970,7 @@ def certidude_list(verbose, show_key_type, show_extensions, show_path, show_sign # e - expired # y - not valid yet # r - revoked - + from humanize import naturaltime from certidude import authority def dump_common(common_name, path, cert): @@ -1074,6 +1072,7 @@ def certidude_cron(): @click.option("-l", "--listen", default="0.0.0.0", help="Listen address") @click.option("-f", "--fork", default=False, is_flag=True, help="Fork to background") def certidude_serve(port, listen, fork): + from setproctitle import setproctitle from certidude.signer import SignServer from certidude import const click.echo("Using configuration from: %s" % const.CONFIG_PATH) diff --git a/requirements.txt b/requirements.txt index b9eed00..a3c3b91 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,6 @@ -click==6.7 -configparser==3.5.0 -cryptography==1.7.2 -falcon==1.1.0 -humanize==0.5.1 -ipaddress==1.0.18 -Jinja2==2.9.5 -Markdown==2.6.8 -pyldap==2.4.28 -python-dateutil==2.6.0 -requests==2.10.0 +click>=6.7 +configparser>=3.5.0 +cryptography>=1.7.1 +Jinja2>=2.8 +requests>=2.12.4 +requests-kerberos>=0.7.0 diff --git a/setup.py b/setup.py index 4884ee6..4e26dd3 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,14 @@ setup( "certidude.api" ], long_description=open("README.rst").read(), + # Include here only stuff required to run certidude client install_requires=[ + "click", + "cryptography", + "configparser", + "jinja2", + "requests", + "requests-kerberos" ], scripts=[ "misc/certidude" From 413029c5ae23e05b91486908fa5f54dcbdd78e96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauri=20V=C3=B5sandi?= Date: Thu, 13 Apr 2017 20:55:46 +0000 Subject: [PATCH 3/9] Fix Travis for Ubuntu 14.04 --- .travis.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index b4a9bab..200a15f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ virtualenv: install: - pip install -r requirements.txt - pip install --editable . - - pip install codecov pytest-cov + - pip install codecov pytest-cov click ipaddress humanize script: - py.test --cov-report xml --cov=certidude tests/ cache: @@ -24,7 +24,4 @@ addons: - python-falcon - python-markdown - python-jinja2 - - python-click - python-configparser - - python-ipaddress - - python-humanize From 721cce05aca20c759d55a660d0c15434e7e4037a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauri=20V=C3=B5sandi?= Date: Thu, 13 Apr 2017 21:03:26 +0000 Subject: [PATCH 4/9] Don't enforce dependency on ldap module --- certidude/user.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/certidude/user.py b/certidude/user.py index 9dbfa93..e33f2da 100644 --- a/certidude/user.py +++ b/certidude/user.py @@ -1,8 +1,6 @@ import click import grp -import ldap -import ldap.sasl import os import pwd from certidude import const, config @@ -65,6 +63,9 @@ class PosixUserManager(object): class DirectoryConnection(object): def __enter__(self): + import ldap + import ldap.sasl + # TODO: Implement simple bind if not os.path.exists(config.LDAP_GSSAPI_CRED_CACHE): raise ValueError("Ticket cache at %s not initialized, unable to " @@ -87,8 +88,7 @@ class ActiveDirectoryUserManager(object): with DirectoryConnection() as conn: ft = config.LDAP_USER_FILTER % username attribs = "cn", "givenName", "sn", "mail", "userPrincipalName" - r = conn.search_s(config.LDAP_BASE, ldap.SCOPE_SUBTREE, - ft.encode("utf-8"), attribs) + r = conn.search_s(config.LDAP_BASE, 2, ft.encode("utf-8"), attribs) for dn, entry in r: if not dn: continue @@ -110,8 +110,7 @@ class ActiveDirectoryUserManager(object): def filter(self, ft): with DirectoryConnection() as conn: attribs = "givenName", "surname", "samaccountname", "cn", "mail", "userPrincipalName" - r = conn.search_s(config.LDAP_BASE, ldap.SCOPE_SUBTREE, - ft.encode("utf-8"), attribs) + r = conn.search_s(config.LDAP_BASE, 2, ft.encode("utf-8"), attribs) for dn,entry in r: if not dn: continue @@ -145,8 +144,7 @@ class ActiveDirectoryUserManager(object): def is_admin(self, user): with DirectoryConnection() as conn: ft = config.LDAP_ADMIN_FILTER % user.name - r = conn.search_s(config.LDAP_BASE, ldap.SCOPE_SUBTREE, - ft.encode("utf-8"), ["cn"]) + r = conn.search_s(config.LDAP_BASE, 2, ft.encode("utf-8"), ["cn"]) for dn, entry in r: if not dn: continue From 0498f959b3566c8b971bdabe28a91a4a1fb8221a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauri=20V=C3=B5sandi?= Date: Thu, 13 Apr 2017 21:10:05 +0000 Subject: [PATCH 5/9] Add pyasn1 as dependency --- .travis.yml | 1 + requirements.txt | 1 + setup.py | 1 + 3 files changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 200a15f..aa897d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,3 +25,4 @@ addons: - python-markdown - python-jinja2 - python-configparser + - python-pyasn1 diff --git a/requirements.txt b/requirements.txt index a3c3b91..a753956 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,5 +2,6 @@ click>=6.7 configparser>=3.5.0 cryptography>=1.7.1 Jinja2>=2.8 +pyasn1>=0.1.9 requests>=2.12.4 requests-kerberos>=0.7.0 diff --git a/setup.py b/setup.py index 4e26dd3..45594db 100644 --- a/setup.py +++ b/setup.py @@ -23,6 +23,7 @@ setup( "cryptography", "configparser", "jinja2", + "pyasn1", "requests", "requests-kerberos" ], From b0f593dae32f762657ec244abc54a5ef85b56e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauri=20V=C3=B5sandi?= Date: Thu, 13 Apr 2017 21:12:59 +0000 Subject: [PATCH 6/9] Use falcon from pip instead of apt for Travis --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index aa897d5..0c6075c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ virtualenv: install: - pip install -r requirements.txt - pip install --editable . - - pip install codecov pytest-cov click ipaddress humanize + - pip install codecov pytest-cov click ipaddress humanize falcon script: - py.test --cov-report xml --cov=certidude tests/ cache: @@ -21,7 +21,6 @@ addons: packages: - python-xattr - python-setproctitle - - python-falcon - python-markdown - python-jinja2 - python-configparser From 6264846284182c88288888df727ae8f573bcacdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauri=20V=C3=B5sandi?= Date: Thu, 13 Apr 2017 21:20:02 +0000 Subject: [PATCH 7/9] Add OpenSSL as dependency for P12 generation --- .travis.yml | 1 + certidude/authority.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0c6075c..ef04624 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,3 +25,4 @@ addons: - python-jinja2 - python-configparser - python-pyasn1 + - python-openssl diff --git a/certidude/authority.py b/certidude/authority.py index cbdee51..d7b016b 100644 --- a/certidude/authority.py +++ b/certidude/authority.py @@ -258,7 +258,6 @@ def generate_pkcs12_bundle(common_name, key_size=4096, owner=None): try: from OpenSSL import crypto except ImportError: - logger.error("For P12 bundles please install pyOpenSSL: pip install pyOpenSSL") raise else: p12 = crypto.PKCS12() From 1c5913ee3b343227ba7fe2658166b502c52f76c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauri=20V=C3=B5sandi?= Date: Thu, 13 Apr 2017 22:30:20 +0000 Subject: [PATCH 8/9] Add dynamic package installation via decorators --- certidude/auth.py | 2 +- certidude/cli.py | 10 +++++++++ certidude/decorators.py | 47 ++++++++++++++++++++++++++++++++++++++++- certidude/firewall.py | 5 ++++- 4 files changed, 61 insertions(+), 3 deletions(-) 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: From bc43fdc4029b847eee826016a799e0ed05c090bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauri=20V=C3=B5sandi?= Date: Thu, 13 Apr 2017 22:37:31 +0000 Subject: [PATCH 9/9] Lazier evaluation for dependencies --- certidude/decorators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certidude/decorators.py b/certidude/decorators.py index 42e65c1..49bebcd 100644 --- a/certidude/decorators.py +++ b/certidude/decorators.py @@ -6,7 +6,6 @@ import os import subprocess import types from datetime import date, time, datetime, timedelta -from certidude.auth import User from urlparse import urlparse logger = logging.getLogger("api") @@ -54,6 +53,7 @@ def event_source(func): class MyEncoder(json.JSONEncoder): def default(self, obj): + from certidude.auth import User if isinstance(obj, ipaddress._IPAddressBase): return str(obj) if isinstance(obj, set):