mirror of
				https://github.com/laurivosandi/certidude
				synced 2025-10-31 01:19:11 +00:00 
			
		
		
		
	Token mechanism fixes:
* Save token secret to config * OpenVPN profile fixes for Ubuntu 16.04 * Raise correct exceptions for invalid tokens * Display token expiration time in local time
This commit is contained in:
		| @@ -1,4 +1,5 @@ | |||||||
| import click | import click | ||||||
|  | import falcon | ||||||
| import logging | import logging | ||||||
| import hashlib | import hashlib | ||||||
| import random | import random | ||||||
| @@ -12,12 +13,6 @@ from certidude.auth import login_required, authorize_admin | |||||||
|  |  | ||||||
| logger = logging.getLogger(__name__) | 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 = ( | KEYWORDS = ( | ||||||
|     (u"Android", u"android"), |     (u"Android", u"android"), | ||||||
|     (u"iPhone", u"iphone"), |     (u"iPhone", u"iphone"), | ||||||
| @@ -36,16 +31,17 @@ class TokenResource(object): | |||||||
|         username = req.get_param("u", required=True) |         username = req.get_param("u", required=True) | ||||||
|         user = User.objects.get(username) |         user = User.objects.get(username) | ||||||
|         csum = hashlib.sha256() |         csum = hashlib.sha256() | ||||||
|         csum.update(SECRET) |         csum.update(config.TOKEN_SECRET) | ||||||
|         csum.update(username) |         csum.update(username) | ||||||
|         csum.update(str(timestamp)) |         csum.update(str(timestamp)) | ||||||
|  |  | ||||||
|         if csum.hexdigest() != req.get_param("c", required=True): |         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: |         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: |         if now > timestamp + config.TOKEN_LIFETIME: | ||||||
|             raise # token expired |             raise falcon.HTTPUnauthorized("Forbidden", "Token expired") | ||||||
|  |  | ||||||
|         # At this point consider token to be legitimate |         # At this point consider token to be legitimate | ||||||
|  |  | ||||||
|         common_name = username |         common_name = username | ||||||
| @@ -83,12 +79,17 @@ class TokenResource(object): | |||||||
|         user = User.objects.get(username) |         user = User.objects.get(username) | ||||||
|         timestamp = int(time()) |         timestamp = int(time()) | ||||||
|         csum = hashlib.sha256() |         csum = hashlib.sha256() | ||||||
|         csum.update(SECRET) |         csum.update(config.TOKEN_SECRET) | ||||||
|         csum.update(username) |         csum.update(username) | ||||||
|         csum.update(str(timestamp)) |         csum.update(str(timestamp)) | ||||||
|         args = "u=%s&t=%d&c=%s" % (username, timestamp, csum.hexdigest()) |         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 = globals() | ||||||
|         context.update(locals()) |         context.update(locals()) | ||||||
|         mailer.send("token.md", to=user, **context) |         mailer.send("token.md", to=user, **context) | ||||||
|   | |||||||
| @@ -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") |     rpm("python-setproctitle pyOpenSSL python-falcon python-humanize python-markdown pyxattr") | ||||||
|     pip("gssapi") |     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") |     template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates") | ||||||
|  |  | ||||||
|     if not directory: |     if not directory: | ||||||
|   | |||||||
| @@ -99,5 +99,6 @@ BUNDLE_FORMAT = cp.get("token", "format") | |||||||
| OPENVPN_PROFILE_TEMPLATE = cp.get("token", "openvpn profile template") | OPENVPN_PROFILE_TEMPLATE = cp.get("token", "openvpn profile template") | ||||||
| TOKEN_URL = cp.get("token", "url") | TOKEN_URL = cp.get("token", "url") | ||||||
| TOKEN_LIFETIME = cp.getint("token", "lifetime") | TOKEN_LIFETIME = cp.getint("token", "lifetime") | ||||||
|  | TOKEN_SECRET = cp.get("token", "secret") | ||||||
|  |  | ||||||
| # TODO: Check if we don't have base or servers | # TODO: Check if we don't have base or servers | ||||||
|   | |||||||
| @@ -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 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 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 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 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 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 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 %} | {% endif %} | ||||||
|  |  | ||||||
| Click [here]({{ config.TOKEN_URL }}?{{ args }}) to claim the token. | 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). | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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 | # Note: don't append comments to lines, Ubuntu 16.04 NetworkManager importer is very picky | ||||||
| client # tls-client; pull | # See more potential problems here: | ||||||
| nobind | # 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 | # OpenVPN gateway(s), uncomment remote-random to load balance | ||||||
| comp-lzo | comp-lzo | ||||||
| proto udp | nobind | ||||||
|  | ;proto udp | ||||||
|  | ;port 1194 | ||||||
| {% if servers %} | {% if servers %} | ||||||
| remote-random | remote-random | ||||||
| {% for server in servers %} | {% for server in servers %} | ||||||
| remote {{ server }} 1194 | remote {{ server }} | ||||||
| {% endfor %} | {% endfor %} | ||||||
| {% else %} | {% else %} | ||||||
| remote 1.2.3.4 1194 | remote 1.2.3.4 | ||||||
| {% endif %} | {% endif %} | ||||||
|  |  | ||||||
| # Virtual network interface settings | # Virtual network interface settings | ||||||
|   | |||||||
| @@ -157,8 +157,13 @@ services template = {{ template_path }}/bootstrap.conf | |||||||
| # Token mechanism allows authority administrator to send invites for users. | # Token mechanism allows authority administrator to send invites for users. | ||||||
| # Token URL could be for example exposed on the internet via proxypass. | # Token URL could be for example exposed on the internet via proxypass. | ||||||
| url = http://{{ common_name }}/api/token | url = http://{{ common_name }}/api/token | ||||||
|  |  | ||||||
|  | # Token lifetime in seconds | ||||||
| lifetime = 3600 | lifetime = 3600 | ||||||
|  |  | ||||||
|  | # Secret for generating and validating tokens, regenerate occasionally | ||||||
|  | secret = {{ token_secret }} | ||||||
|  |  | ||||||
| # Profile format, uncomment specific one to enable token mechanism | # Profile format, uncomment specific one to enable token mechanism | ||||||
| format = | format = | ||||||
| ;format = p12 | ;format = p12 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user