diff --git a/config-sample.py b/config-sample.py index b85b227..6946b91 100644 --- a/config-sample.py +++ b/config-sample.py @@ -22,6 +22,10 @@ SITE_ROOT = os.path.join(_basedir, "site", "www") # E-mail addresses for site notifications (i.e. new comments). NOTIFY_ADDRESS = ["root@localhost"] +# Set this to true if you want your app to force use of SSL. This will also turn +# on Flask's secure-only session cookies. +FORCE_SSL = False + # Secret key used for session cookie signing. Make this long and hard to guess. # # Tips for creating a strong secret key: @@ -111,4 +115,4 @@ COMMENT_TIME_FORMAT = "%A, %B %d %Y @ %I:%M %p" # We use Gravatar for comments if the user provides an e-mail address. Specify # the URL to a fallback image to use in case they don't have a gravatar. -COMMENT_DEFAULT_AVATAR = "" \ No newline at end of file +COMMENT_DEFAULT_AVATAR = "" diff --git a/requirements.txt b/requirements.txt index e3329c7..f4bb3fc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ flask +flask-sslify redis bcrypt pillow diff --git a/rophako/__init__.py b/rophako/__init__.py index b94f75d..55fa81b 100644 --- a/rophako/__init__.py +++ b/rophako/__init__.py @@ -1,6 +1,7 @@ __version__ = '0.01' from flask import Flask, g, request, session, render_template, send_file, abort +from flask_sslify import SSLify import jinja2 import os.path import time @@ -14,6 +15,11 @@ app = Flask(__name__, app.DEBUG = config.DEBUG app.secret_key = config.SECRET_KEY +# Security? +if config.FORCE_SSL: + app.SESSION_COOKIE_SECURE = True + sslify = SSLify(app) + # Load all the blueprints! from rophako.modules.admin import mod as AdminModule from rophako.modules.account import mod as AccountModule @@ -134,4 +140,4 @@ def not_found(error): # Domain specific endpoints. if config.SITE_NAME == "kirsle.net": - import rophako.modules.kirsle_legacy \ No newline at end of file + import rophako.modules.kirsle_legacy diff --git a/rophako/model/comment.py b/rophako/model/comment.py index 9fdb4b6..148cddd 100644 --- a/rophako/model/comment.py +++ b/rophako/model/comment.py @@ -226,7 +226,7 @@ def gravatar(email): } if default: params["d"] = default - url = "http://www.gravatar.com/avatar/" + hashlib.md5(email.lower()).hexdigest() + "?" + url = "//www.gravatar.com/avatar/" + hashlib.md5(email.lower()).hexdigest() + "?" url += urllib.urlencode(params) return url - return "" \ No newline at end of file + return "" diff --git a/rophako/modules/kirsle_legacy.py b/rophako/modules/kirsle_legacy.py index 8bccbb1..55e97a3 100644 --- a/rophako/modules/kirsle_legacy.py +++ b/rophako/modules/kirsle_legacy.py @@ -7,7 +7,7 @@ import re import os from rophako import app -from rophako.utils import template +from rophako.utils import template, login_required import rophako.model.blog as Blog import rophako.jsondb as JsonDB @@ -95,3 +95,14 @@ def legacy_download(): @app.route("/.html") def legacy_url(page): return redirect("/{}".format(page), code=301) + +@app.route("/ssl_test") +@login_required +def ssl_test(): + criteria = [ + request.is_secure, + app.debug, + request.headers.get("X-Forwarded-Proto", "http") == "https" + ] + + return str(criteria) diff --git a/rophako/www/comment/index.inc.html b/rophako/www/comment/index.inc.html index 90f3d75..c56aaf8 100644 --- a/rophako/www/comment/index.inc.html +++ b/rophako/www/comment/index.inc.html @@ -9,7 +9,7 @@ There {% if comments|length == 1 %}is{% else %}are{% endif %} {% for comment in comments %}
- {% if comment["image"] and (comment["image"].startswith('http:') or comment["image"].startswith('https:')) %} + {% if comment["image"] and (comment["image"].startswith('http:') or comment["image"].startswith('https:') or comment["image"].startswith('//')) %} Avatar {% elif comment["image"] %} Avatar @@ -87,4 +87,4 @@ There {% if comments|length == 1 %}is{% else %}are{% endif %}
- \ No newline at end of file + diff --git a/runserver.py b/runserver.py index 3642f13..a9e67a5 100644 --- a/runserver.py +++ b/runserver.py @@ -1,4 +1,47 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import sys +import argparse from rophako import app -app.run(host='0.0.0.0', debug=True, port=2006) \ No newline at end of file + +parser = argparse.ArgumentParser(description="Rophako") +parser.add_argument( + "--port", "-p", + type=int, + help="Port to listen on", + default=2006, +) +parser.add_argument( + "--key", "-k", + type=str, + help="SSL private key file. Providing this option will turn on SSL mode " \ + + "(and will require pyOpenSSL to be installed).", +) +parser.add_argument( + "--cert", "-c", + type=str, + help="SSL certificate file.", +) +args = parser.parse_args() + +if __name__ == '__main__': + flask_options = dict( + host='0.0.0.0', + debug=True, + port=args.port, + threaded=True, + ) + + if args.key and args.cert: + from OpenSSL import SSL + context = SSL.Context(SSL.SSLv23_METHOD) + context.use_privatekey_file(args.key) + context.use_certificate_file(args.cert) + app.config['SESSION_COOKIE_SECURE'] = True + flask_options["ssl_context"] = context + + app.run(**flask_options) +