From: David Galloway Date: Tue, 22 Mar 2016 21:52:58 +0000 (-0400) Subject: gateway: Write universal 'auth-openvpn' user auth script X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=5ba53cb12ed6278397c69226ec6148eeb14145de;p=ceph-cm-ansible.git gateway: Write universal 'auth-openvpn' user auth script Signed-off-by: David Galloway --- diff --git a/roles/gateway/tasks/users.yml b/roles/gateway/tasks/users.yml index 497d261..00d071b 100644 --- a/roles/gateway/tasks/users.yml +++ b/roles/gateway/tasks/users.yml @@ -11,3 +11,11 @@ owner: root group: root mode: 0644 + +- name: Upload auth-openvpn script + template: + src: auth-openvpn + dest: "{{ openvpn_data_dir }}/auth-openvpn" + owner: root + group: root + mode: 0755 diff --git a/roles/gateway/templates/auth-openvpn b/roles/gateway/templates/auth-openvpn new file mode 100644 index 0000000..bd82544 --- /dev/null +++ b/roles/gateway/templates/auth-openvpn @@ -0,0 +1,91 @@ +#!/usr/bin/python + +import hashlib +import logging +import logging.handlers +import os +import re +import sys +import time + +log = logging.getLogger('auth-openvpn') + +def authenticate(): + # annoy attackers + time.sleep(1) + + path = sys.argv[1] + with file(path, 'rb') as f: + user = f.readline(8192) + assert user.endswith('\n') + user = user[:-1] + assert user + secret = f.readline(8192) + assert secret.endswith('\n') + secret = secret[:-1] + assert secret + + # From openvpn(8): + # + # To protect against a client passing a maliciously formed username or + # password string, the username string must consist only of these + # characters: alphanumeric, underbar ('_'), dash ('-'), dot ('.'), or + # at ('@'). The password string can consist of any printable + # characters except for CR or LF. Any illegal characters in either the + # username or password string will be converted to underbar ('_'). + # + # We'll just redo that quickly for usernames, to ensure they are safe. + + user = re.sub(r'[^a-zA-Z0-9_.@-]', '_', user) + + def find_user(wanted): + with file('{{ openvpn_data_dir }}/users') as f: + for line in f: + assert line.endswith('\n') + line = line[:-1] + (username, salt, correct) = line.split(' ', 2) + if username == wanted: + return (salt, correct) + + # these will never match + log.error('User not found: %r', wanted) + salt = 'not-found' + correct = 64*'x' + return (salt, correct) + + (salt, correct) = find_user(user) + + inner = hashlib.new('sha256') + inner.update(salt) + inner.update(secret) + outer = hashlib.new('sha256') + outer.update(inner.digest()) + outer.update(salt) + attempt = outer.hexdigest() + + if attempt != correct: + log.error('{prog}: invalid auth for user {user!r}.'.format(prog=os.path.basename(sys.argv[0]), user=user)) + sys.exit(1) + +def main(): + handler = logging.handlers.SysLogHandler( + address='/dev/log', + facility=logging.handlers.SysLogHandler.LOG_DAEMON, + ) + fmt = logging.Formatter('%(name)s: %(message)s') + handler.setFormatter(fmt) + logging.basicConfig() + root = logging.getLogger('') + root.addHandler(handler) + log.setLevel(logging.INFO) + + try: + authenticate() + except SystemExit: + raise + except: + log.exception('Unhandled error: ') + raise + +if __name__ == '__main__': + sys.exit(main())