diff --git a/certidude/api/token.py b/certidude/api/token.py index 34c9c90..6fda346 100644 --- a/certidude/api/token.py +++ b/certidude/api/token.py @@ -1,4 +1,5 @@ import click +import falcon import logging import hashlib import random @@ -12,12 +13,6 @@ from certidude.auth import login_required, authorize_admin logger = logging.getLogger(__name__) -chars = string.ascii_letters + string.digits + '!@#$%^&*()' -SECRET = ''.join(random.choice(chars) for i in range(32)) - -click.echo("Token secret: %s" % SECRET) - - KEYWORDS = ( (u"Android", u"android"), (u"iPhone", u"iphone"), @@ -36,16 +31,17 @@ class TokenResource(object): username = req.get_param("u", required=True) user = User.objects.get(username) csum = hashlib.sha256() - csum.update(SECRET) + csum.update(config.TOKEN_SECRET) csum.update(username) csum.update(str(timestamp)) if csum.hexdigest() != req.get_param("c", required=True): - raise # TODO + raise falcon.HTTPUnauthorized("Forbidden", "Invalid token supplied, did you copy-paste link correctly?") if now < timestamp: - raise # Token not valid yet + raise falcon.HTTPUnauthorized("Forbidden", "Token not valid yet, are you sure server clock is correct?") if now > timestamp + config.TOKEN_LIFETIME: - raise # token expired + raise falcon.HTTPUnauthorized("Forbidden", "Token expired") + # At this point consider token to be legitimate common_name = username @@ -83,12 +79,17 @@ class TokenResource(object): user = User.objects.get(username) timestamp = int(time()) csum = hashlib.sha256() - csum.update(SECRET) + csum.update(config.TOKEN_SECRET) csum.update(username) csum.update(str(timestamp)) args = "u=%s&t=%d&c=%s" % (username, timestamp, csum.hexdigest()) - token_created = datetime.utcfromtimestamp(timestamp) - token_expires = datetime.utcfromtimestamp(timestamp + config.TOKEN_LIFETIME) + + # Token lifetime in local time, to select timezone: dpkg-reconfigure tzdata + token_created = datetime.fromtimestamp(timestamp) + token_expires = datetime.fromtimestamp(timestamp + config.TOKEN_LIFETIME) + with open("/etc/timezone") as fh: + token_timezone = fh.read().strip() + context = globals() context.update(locals()) mailer.send("token.md", to=user, **context) diff --git a/certidude/cli.py b/certidude/cli.py index 7d90114..dff9870 100755 --- a/certidude/cli.py +++ b/certidude/cli.py @@ -790,6 +790,9 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country, rpm("python-setproctitle pyOpenSSL python-falcon python-humanize python-markdown pyxattr") pip("gssapi") + # Generate secret for tokens + token_secret = ''.join(random.choice(string.letters + string.digits + '!@#$%^&*()') for i in range(50)) + template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates") if not directory: diff --git a/certidude/config.py b/certidude/config.py index e1b7a95..c9cf6cb 100644 --- a/certidude/config.py +++ b/certidude/config.py @@ -99,5 +99,6 @@ BUNDLE_FORMAT = cp.get("token", "format") OPENVPN_PROFILE_TEMPLATE = cp.get("token", "openvpn profile template") TOKEN_URL = cp.get("token", "url") TOKEN_LIFETIME = cp.getint("token", "lifetime") +TOKEN_SECRET = cp.get("token", "secret") # TODO: Check if we don't have base or servers diff --git a/certidude/templates/mail/token.md b/certidude/templates/mail/token.md index 4a1eccb..fcf7c15 100644 --- a/certidude/templates/mail/token.md +++ b/certidude/templates/mail/token.md @@ -9,10 +9,11 @@ To set up OpenVPN for your device: * for Android install [OpenVPN Connect](https://play.google.com/store/apps/details?id=de.blinkt.openvpn) app. After importing the OpenVPN profile in OpenVPN application and delete the downloaded .ovpn file. * for iOS device install [OpenVPN Connect](https://itunes.apple.com/us/app/openvpn-connect/id590379981) app. Tap on the token URL below, it should be automatically opened with OpenVPN Connect app. Tap connect to establish connection. * for Mac OS X download [Tunnelblick](https://tunnelblick.net/downloads.html) -* for Ubuntu and Fedora install OpenVPN plugin for NetworkManager. Open network settings, add connection and select "Import from file ...". Supply the file retrieved via the token URL below. -* for Windows you need to install OpenVPN community edition from [here](https://swupdate.openvpn.org/community/releases/openvpn-install-2.3.14-I601-x86_64.exe) and TAP driver from [here](https://swupdate.openvpn.org/community/releases/tap-windows-9.21.2.exe) +* for Ubuntu install [OpenVPN plugin for NetworkManager](apt://network-manager-openvpn-gnome), click on the token link below to download OpenVPN profile. Click on the NetworkManager icon, select "Edit Connections...", click on "Add" button to add a connection. From the dropdown menu select "Import a saved VPN configuration..." and supply the downloaded file. +* for Fedora install OpenVPN plugin for NetworkManager. Open network settings, add connection and select "Import a saved VPN configuration...". Supply the file retrieved via the token URL below. +* for Windows install OpenVPN community edition from [here](https://swupdate.openvpn.org/community/releases/openvpn-install-2.3.14-I601-x86_64.exe) and TAP driver from [here](https://swupdate.openvpn.org/community/releases/tap-windows-9.21.2.exe) {% endif %} Click [here]({{ config.TOKEN_URL }}?{{ args }}) to claim the token. -Token is usable until {{ token_expires }} (UTC). +Token is usable until {{ token_expires }} ({{ token_timezone }} time). diff --git a/certidude/templates/openvpn-client.conf b/certidude/templates/openvpn-client.conf index 8948a54..16781b7 100644 --- a/certidude/templates/openvpn-client.conf +++ b/certidude/templates/openvpn-client.conf @@ -1,19 +1,24 @@ -# Copy this file to /etc/certidude/template.ovpn and customize gateway IP addresses +# Copy this file to /etc/certidude/template.ovpn and customize as you see fit -# Run as client -client # tls-client; pull -nobind +# Note: don't append comments to lines, Ubuntu 16.04 NetworkManager importer is very picky +# See more potential problems here: +# https://askubuntu.com/questions/761684/error-the-plugin-does-not-support-import-capability-when-attempting-to-import + +# Run as OpenVPN client, pull routes, DNS server, DNS suffix from gateway +client # OpenVPN gateway(s), uncomment remote-random to load balance comp-lzo -proto udp +nobind +;proto udp +;port 1194 {% if servers %} remote-random {% for server in servers %} -remote {{ server }} 1194 +remote {{ server }} {% endfor %} {% else %} -remote 1.2.3.4 1194 +remote 1.2.3.4 {% endif %} # Virtual network interface settings diff --git a/certidude/templates/server/server.conf b/certidude/templates/server/server.conf index 532e3b7..510ec69 100644 --- a/certidude/templates/server/server.conf +++ b/certidude/templates/server/server.conf @@ -157,8 +157,13 @@ services template = {{ template_path }}/bootstrap.conf # Token mechanism allows authority administrator to send invites for users. # Token URL could be for example exposed on the internet via proxypass. url = http://{{ common_name }}/api/token + +# Token lifetime in seconds lifetime = 3600 +# Secret for generating and validating tokens, regenerate occasionally +secret = {{ token_secret }} + # Profile format, uncomment specific one to enable token mechanism format = ;format = p12