From f93ce70d6d1025c24f73ed502ab4a4e10cdd939c Mon Sep 17 00:00:00 2001 From: Priit Laes Date: Tue, 1 Sep 2015 11:02:00 +0000 Subject: [PATCH 1/4] Add factory function to create wsgi app - kills some duplicate code --- certidude/api.py | 18 +++++++++++++++++- certidude/cli.py | 18 ++---------------- certidude/wsgi.py | 29 +++++++---------------------- 3 files changed, 26 insertions(+), 39 deletions(-) diff --git a/certidude/api.py b/certidude/api.py index 702d698..0d5bc25 100644 --- a/certidude/api.py +++ b/certidude/api.py @@ -7,7 +7,7 @@ import types import urllib.request import click from time import sleep -from certidude.wrappers import Request, Certificate +from certidude.wrappers import Request, Certificate, CertificateAuthorityConfig from certidude.auth import login_required from certidude.mailer import Mailer from pyasn1.codec.der import decoder @@ -356,3 +356,19 @@ class ApplicationConfigurationResource(CertificateAuthorityBase): resp.append_header("Content-Disposition", "attachment; filename=%s.ovpn" % cn) resp.body = Template(open("/etc/openvpn/%s.template" % ca.slug).read()).render(ctx) + +def certidude_app(): + config = CertificateAuthorityConfig() + + app = falcon.API() + app.add_route("/api/{ca}/ocsp/", CertificateStatusResource(config)) + app.add_route("/api/{ca}/signed/{cn}/openvpn", ApplicationConfigurationResource(config)) + app.add_route("/api/{ca}/certificate/", CertificateAuthorityResource(config)) + app.add_route("/api/{ca}/revoked/", RevocationListResource(config)) + app.add_route("/api/{ca}/signed/{cn}/", SignedCertificateDetailResource(config)) + app.add_route("/api/{ca}/signed/", SignedCertificateListResource(config)) + app.add_route("/api/{ca}/request/{cn}/", RequestDetailResource(config)) + app.add_route("/api/{ca}/request/", RequestListResource(config)) + app.add_route("/api/{ca}/", IndexResource(config)) + + return app diff --git a/certidude/cli.py b/certidude/cli.py index 978eb98..7802bdb 100755 --- a/certidude/cli.py +++ b/certidude/cli.py @@ -798,30 +798,16 @@ def certidude_serve(user, port, listen, enable_signature): click.echo("Serving API at %s:%d" % (listen, port)) import pwd - import falcon from wsgiref.simple_server import make_server, WSGIServer from socketserver import ThreadingMixIn - from certidude.api import CertificateAuthorityResource, \ - RequestDetailResource, RequestListResource, \ - SignedCertificateDetailResource, SignedCertificateListResource, \ - RevocationListResource, IndexResource, ApplicationConfigurationResource, \ - CertificateStatusResource + from certidude.api import certidude_app class ThreadingWSGIServer(ThreadingMixIn, WSGIServer): pass click.echo("Listening on %s:%d" % (listen, port)) - app = falcon.API() - app.add_route("/api/{ca}/ocsp/", CertificateStatusResource(config)) - app.add_route("/api/{ca}/signed/{cn}/openvpn", ApplicationConfigurationResource(config)) - app.add_route("/api/{ca}/certificate/", CertificateAuthorityResource(config)) - app.add_route("/api/{ca}/revoked/", RevocationListResource(config)) - app.add_route("/api/{ca}/signed/{cn}/", SignedCertificateDetailResource(config)) - app.add_route("/api/{ca}/signed/", SignedCertificateListResource(config)) - app.add_route("/api/{ca}/request/{cn}/", RequestDetailResource(config)) - app.add_route("/api/{ca}/request/", RequestListResource(config)) - app.add_route("/api/{ca}/", IndexResource(config)) + app = certidude_app() app.add_sink(StaticResource(os.path.join(os.path.dirname(__file__), "static"))) httpd = make_server(listen, port, app, ThreadingWSGIServer) diff --git a/certidude/wsgi.py b/certidude/wsgi.py index 241f6cd..5b546c4 100644 --- a/certidude/wsgi.py +++ b/certidude/wsgi.py @@ -1,29 +1,14 @@ +""" + certidude.wsgi + ~~~~~~~~~~~~~~ + Certidude web app factory for WSGI-compatible web servers +""" import os -import falcon -from certidude.wrappers import CertificateAuthorityConfig -from certidude.api import CertificateAuthorityResource, \ - RequestDetailResource, RequestListResource, \ - SignedCertificateDetailResource, SignedCertificateListResource, \ - RevocationListResource, IndexResource, ApplicationConfigurationResource, \ - CertificateStatusResource +from certidude.api import certidude_app -# TODO: deduplicate routing code # TODO: set up /run/certidude/api paths and permissions - -config = CertificateAuthorityConfig() - assert os.getenv("PUSH_SUBSCRIBE"), "Please set PUSH_SUBSCRIBE to your web server's subscription URL" assert os.getenv("PUSH_PUBLISH"), "Please set PUSH_PUBLISH to your web server's publishing URL" -app = falcon.API() -app.add_route("/api/{ca}/ocsp/", CertificateStatusResource(config)) -app.add_route("/api/{ca}/signed/{cn}/openvpn", ApplicationConfigurationResource(config)) -app.add_route("/api/{ca}/certificate/", CertificateAuthorityResource(config)) -app.add_route("/api/{ca}/revoked/", RevocationListResource(config)) -app.add_route("/api/{ca}/signed/{cn}/", SignedCertificateDetailResource(config)) -app.add_route("/api/{ca}/signed/", SignedCertificateListResource(config)) -app.add_route("/api/{ca}/request/{cn}/", RequestDetailResource(config)) -app.add_route("/api/{ca}/request/", RequestListResource(config)) -app.add_route("/api/{ca}/", IndexResource(config)) - +app = certidude_app() From 46fd8a2385366c081a32223d8019e1dc7a016241 Mon Sep 17 00:00:00 2001 From: Priit Laes Date: Wed, 2 Sep 2015 05:35:18 +0000 Subject: [PATCH 2/4] Move all falcon-specific stuff away from cli --- certidude/api.py | 28 +++++++++++++++++++++++++++- certidude/cli.py | 29 ++--------------------------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/certidude/api.py b/certidude/api.py index 0d5bc25..de51c7e 100644 --- a/certidude/api.py +++ b/certidude/api.py @@ -1,6 +1,7 @@ import re import falcon import ipaddress +import mimetypes import os import json import types @@ -355,7 +356,32 @@ class ApplicationConfigurationResource(CertificateAuthorityBase): resp.append_header("Content-Type", "application/ovpn") resp.append_header("Content-Disposition", "attachment; filename=%s.ovpn" % cn) resp.body = Template(open("/etc/openvpn/%s.template" % ca.slug).read()).render(ctx) - + + +class StaticResource(object): + def __init__(self, root): + self.root = os.path.realpath(root) + + def __call__(self, req, resp): + + path = os.path.realpath(os.path.join(self.root, req.path[1:])) + if not path.startswith(self.root): + raise falcon.HTTPForbidden + + print("Serving:", path) + if os.path.exists(path): + content_type, content_encoding = mimetypes.guess_type(path) + if content_type: + resp.append_header("Content-Type", content_type) + if content_encoding: + resp.append_header("Content-Encoding", content_encoding) + resp.append_header("Content-Disposition", "attachment") + resp.stream = open(path, "rb") + else: + resp.status = falcon.HTTP_404 + resp.body = "File '%s' not found" % req.path + + def certidude_app(): config = CertificateAuthorityConfig() diff --git a/certidude/cli.py b/certidude/cli.py index 7802bdb..f705c66 100755 --- a/certidude/cli.py +++ b/certidude/cli.py @@ -3,9 +3,7 @@ import asyncore import click -import falcon import logging -import mimetypes import netifaces import os import pwd @@ -761,30 +759,6 @@ def certidude_sign(common_name, overwrite, lifetime): click.echo("Added extension %s: %s" % (key, value)) click.echo() -class StaticResource(object): - def __init__(self, root): - self.root = os.path.realpath(root) - click.echo("Serving static from: %s" % self.root) - - def __call__(self, req, resp): - - path = os.path.realpath(os.path.join(self.root, req.path[1:])) - if not path.startswith(self.root): - raise falcon.HTTPForbidden - - print("Serving:", path) - if os.path.exists(path): - content_type, content_encoding = mimetypes.guess_type(path) - if content_type: - resp.append_header("Content-Type", content_type) - if content_encoding: - resp.append_header("Content-Encoding", content_encoding) - resp.append_header("Content-Disposition", "attachment") - resp.stream = open(path, "rb") - else: - resp.status = falcon.HTTP_404 - resp.body = "File '%s' not found" % req.path - @click.command("serve", help="Run built-in HTTP server") @click.option("-u", "--user", default="certidude", help="Run as user") @click.option("-p", "--port", default=80, help="Listen port") @@ -800,7 +774,7 @@ def certidude_serve(user, port, listen, enable_signature): import pwd from wsgiref.simple_server import make_server, WSGIServer from socketserver import ThreadingMixIn - from certidude.api import certidude_app + from certidude.api import certidude_app, StaticResource class ThreadingWSGIServer(ThreadingMixIn, WSGIServer): pass @@ -810,6 +784,7 @@ def certidude_serve(user, port, listen, enable_signature): app = certidude_app() app.add_sink(StaticResource(os.path.join(os.path.dirname(__file__), "static"))) + httpd = make_server(listen, port, app, ThreadingWSGIServer) if user: _, _, uid, gid, gecos, root, shell = pwd.getpwnam(user) From 0435b802af1ef477eb8f14d2264bd9c526ef5257 Mon Sep 17 00:00:00 2001 From: Priit Laes Date: Wed, 2 Sep 2015 05:37:45 +0000 Subject: [PATCH 3/4] Kill unused imports from api --- certidude/api.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/certidude/api.py b/certidude/api.py index de51c7e..67ed433 100644 --- a/certidude/api.py +++ b/certidude/api.py @@ -5,15 +5,12 @@ import mimetypes import os import json import types -import urllib.request import click from time import sleep from certidude.wrappers import Request, Certificate, CertificateAuthorityConfig from certidude.auth import login_required -from certidude.mailer import Mailer from pyasn1.codec.der import decoder from datetime import datetime, date -from OpenSSL import crypto from jinja2 import Environment, PackageLoader, Template env = Environment(loader=PackageLoader("certidude", "templates")) From 4a94715c680bec3039dca243266b110644d4b012 Mon Sep 17 00:00:00 2001 From: Priit Laes Date: Thu, 3 Sep 2015 09:00:45 +0000 Subject: [PATCH 4/4] Add workaround for chroot issues --- certidude/cli.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/certidude/cli.py b/certidude/cli.py index f705c66..06837dc 100755 --- a/certidude/cli.py +++ b/certidude/cli.py @@ -786,7 +786,13 @@ def certidude_serve(user, port, listen, enable_signature): app.add_sink(StaticResource(os.path.join(os.path.dirname(__file__), "static"))) httpd = make_server(listen, port, app, ThreadingWSGIServer) + if user: + # Load required utils which cannot be imported from chroot + # TODO: Figure out better approach + from jinja2.debug import make_traceback as _make_traceback + "".encode("charmap") + _, _, uid, gid, gecos, root, shell = pwd.getpwnam(user) if uid == 0: click.echo("Please specify unprivileged user") @@ -796,7 +802,7 @@ def certidude_serve(user, port, listen, enable_signature): os.setuid(uid) os.umask(0o007) elif os.getuid() == 0: - click.echo("Warning: running as root, this is not reccommended!") + click.echo("Warning: running as root, this is not recommended!") httpd.serve_forever() @click.group("strongswan", help="strongSwan helpers")