def get_link_user(self, org_ids): from pritunl import organization for org_id in org_ids: org = organization.get_by_id(org_id) if not org: continue usr = org.find_user(resource_id=self.id) if not usr: logger.info('Creating host link user', 'host', host_id=self.id, ) usr = org.new_user(name=HOST_USER_PREFIX + str(self.id), type=CERT_SERVER, resource_id=self.id) journal.entry( journal.USER_CREATE, usr.journal_data, event_long='User created for host linking', ) usr.audit_event('user_created', 'User created for host linking') return usr raise ValueError('No orgs exists in link server')
def user_uri_key_page_get(short_code): remote_addr = utils.get_remote_addr() doc = _find_doc({ 'short_id': short_code, }, one_time=True) if not doc: return flask.abort(404) org = organization.get_by_id(doc['org_id']) usr = org.get_user(id=doc['user_id']) if usr.disabled: return flask.abort(403) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User temporary profile downloaded from pritunl client', ) usr.audit_event('user_profile', 'User temporary profile downloaded from pritunl client', remote_addr=remote_addr, ) keys = {} for server in usr.iter_servers(): key = usr.build_key_conf(server.id) keys[key['name']] = key['conf'] return utils.jsonify(keys)
def user_linked_key_conf_get(key_id, server_id): doc = _find_doc({ 'key_id': key_id, }) if not doc: return flask.abort(404) org = organization.get_by_id(doc['org_id']) if not org: return flask.abort(404) user = org.get_user(id=doc['user_id']) if not user: return flask.abort(404) if user.disabled: return flask.abort(403) key_conf = user.build_key_conf(server_id) user.audit_event('user_profile', 'User profile downloaded with temporary profile link', remote_addr=utils.get_remote_addr(), ) response = flask.Response(response=key_conf['conf'], mimetype='application/ovpn') response.headers.add('Content-Disposition', 'attachment; filename="%s"' % key_conf['name']) return response
def sso_callback_get(): state = flask.request.args.get('state') user = flask.request.args.get('user') tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_and_modify(query={ '_id': state, }, remove=True) if not doc: return flask.abort(404) org_id = settings.app.sso_org if not org_id: return flask.abort(405) org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=user) if not usr: usr = org.new_user(name=user, email=user, type=CERT_CLIENT) key_link = org.create_user_key_link(usr.id) return flask.redirect(flask.request.url_root[:-1] + key_link['view_url'])
def org_put(org_id): if settings.app.demo_mode: return utils.demo_blocked() org = organization.get_by_id(org_id) org.name = utils.filter_str(flask.request.json['name']) auth_api = flask.request.json.get('auth_api', False) if auth_api: org.auth_api = True if not org.auth_token: org.generate_auth_token() if not org.auth_secret: org.generate_auth_secret() else: org.auth_api = False org.auth_token = None org.auth_secret = None if flask.request.json.get('auth_token') == True: org.generate_auth_token() if flask.request.json.get('auth_secret') == True: org.generate_auth_secret() org.commit() event.Event(type=ORGS_UPDATED) return utils.jsonify(org.dict())
def key_sync_get(org_id, user_id, server_id, key_hash): utils.rand_sleep() if not settings.local.sub_active: return utils.response('', status_code=480) auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: return flask.abort(401) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: return flask.abort(401) except ValueError: return flask.abort(401) org = organization.get_by_id(org_id) if not org: return flask.abort(404) user = org.get_user(id=user_id) if not user: return flask.abort(404) elif not user.sync_secret: return flask.abort(404) auth_string = '&'.join([ auth_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path] + ([flask.request.data] if flask.request.data else [])) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: return flask.abort(401) auth_test_signature = base64.b64encode(hmac.new( user.sync_secret.encode(), auth_string, hashlib.sha256).digest()) if auth_signature != auth_test_signature: return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }, w=0) except pymongo.errors.DuplicateKeyError: return flask.abort(401) key_conf = user.sync_conf(server_id, key_hash) if key_conf: return utils.response(key_conf['conf']) return utils.response('')
def org_put(org_id): org = organization.get_by_id(org_id) name = utils.filter_str(flask.request.json['name']) org.name = name org.commit(org.changed) event.Event(type=ORGS_UPDATED) return utils.jsonify(org.dict())
def user_post(org_id): org = organization.get_by_id(org_id) users = [] if isinstance(flask.request.json, list): users_data = flask.request.json else: users_data = [flask.request.json] for user_data in users_data: name = utils.filter_str(user_data['name']) email = utils.filter_str(user_data.get('email')) disabled = user_data.get('disabled') user = org.new_user(type=CERT_CLIENT, name=name, email=email, disabled=disabled) users.append(user.dict()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) if isinstance(flask.request.json, list): logger.LogEntry(message='Created %s new users.' % len( flask.request.json)) return utils.jsonify(users) else: logger.LogEntry(message='Created new user "%s".' % users[0]['name']) return utils.jsonify(users[0])
def user_linked_key_conf_get(key_id, server_id): doc = _find_doc({"key_id": key_id}) if not doc: return flask.abort(404) org = organization.get_by_id(doc["org_id"]) if not org: return flask.abort(404) user = org.get_user(id=doc["user_id"]) if not user: return flask.abort(404) if user.disabled: return flask.abort(403) key_conf = user.build_key_conf(server_id) user.audit_event( "user_profile", "User profile downloaded with temporary profile link", remote_addr=utils.get_remote_addr() ) response = flask.Response(response=key_conf["conf"], mimetype="application/ovpn") response.headers.add("Content-Disposition", 'attachment; filename="%s"' % key_conf["name"]) return response
def sso_duo_post(): sso_mode = settings.app.sso token = utils.filter_str(flask.request.json.get("token")) or None passcode = utils.filter_str(flask.request.json.get("passcode")) or None if not token or not passcode: return utils.jsonify({"error": TOKEN_INVALID, "error_msg": TOKEN_INVALID_MSG}, 401) tokens_collection = mongo.get_collection("sso_tokens") doc = tokens_collection.find_one({"_id": token}) if not doc or doc["_id"] != token: return utils.jsonify({"error": TOKEN_INVALID, "error_msg": TOKEN_INVALID_MSG}, 401) username = doc["username"] email = doc["email"] org_id = doc["org_id"] groups = doc["groups"] duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type="Key", passcode=passcode, ) valid = duo_auth.authenticate() if not valid: return utils.jsonify({"error": PASSCODE_INVALID, "error_msg": PASSCODE_INVALID_MSG}, 401) org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user( name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, groups=list(groups) if groups else None ) usr.audit_event("user_created", "User created with single sign-on", remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit("groups") if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit("auth_type") key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event("user_profile", "User profile viewed from single sign-on", remote_addr=utils.get_remote_addr()) return utils.jsonify({"redirect": utils.get_url_root() + key_link["view_url"]}, 200)
def _get_onc_archive(org_id, user_id): org = organization.get_by_id(org_id) usr = org.get_user(user_id) key_archive = usr.build_onc_archive() response = flask.Response(response=key_archive, mimetype="application/octet-stream") response.headers.add("Content-Disposition", 'attachment; filename="%s.zip"' % usr.name) return (usr, response)
def server_org_delete(server_id, org_id): if settings.app.demo_mode: return utils.demo_blocked() svr = server.get_by_id(server_id, fields=('_id', 'status', 'network', 'network_start', 'network_end', 'primary_organization', 'primary_user', 'organizations', 'routes', 'ipv6')) org = organization.get_by_id(org_id, fields=('_id')) if svr.status == ONLINE: return utils.jsonify({ 'error': SERVER_NOT_OFFLINE, 'error_msg': SERVER_NOT_OFFLINE_DETACH_ORG_MSG, }, 400) svr.remove_org(org) svr.commit(svr.changed) event.Event(type=SERVERS_UPDATED) event.Event(type=SERVER_ROUTES_UPDATED, resource_id=svr.id) event.Event(type=SERVER_ORGS_UPDATED, resource_id=svr.id) event.Event(type=USERS_UPDATED, resource_id=org.id) return utils.jsonify({})
def user_otp_secret_put(org_id, user_id): org = organization.get_by_id(org_id) user = org.get_user(user_id) user.generate_otp_secret() user.commit() event.Event(type=USERS_UPDATED, resource_id=org.id) return utils.jsonify(user.dict())
def _auth_radius(username, password): valid, org_id = sso.verify_radius(username, password) if not valid: return utils.jsonify({"error": AUTH_INVALID, "error_msg": AUTH_INVALID_MSG}, 401) if not org_id: org_id = settings.app.sso_org org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, type=CERT_CLIENT, auth_type=RADIUS_AUTH) usr.audit_event("user_created", "User created with single sign-on", remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return utils.jsonify({"error": AUTH_DISABLED, "error_msg": AUTH_DISABLED_MSG}, 403) if usr.auth_type != RADIUS_AUTH: usr.auth_type = RADIUS_AUTH usr.set_pin(None) usr.commit(("auth_type", "pin")) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event("user_profile", "User profile viewed from single sign-on", remote_addr=utils.get_remote_addr()) return utils.jsonify({"redirect": flask.request.url_root[:-1] + key_link["view_url"]}, 202)
def user_audit_get(org_id, user_id): if org_id == 'admin' and user_id == 'admin': return utils.jsonify(auth.get_audit_events()) org = organization.get_by_id(org_id) user = org.get_user(user_id) return utils.jsonify(user.get_audit_events())
def org_get(org_id=None): if org_id: return utils.jsonify(organization.get_by_id(org_id).dict()) orgs = [] for org in organization.iter_orgs_dict(): orgs.append(org) return utils.jsonify(orgs)
def _get_key_archive(org_id, user_id): org = organization.get_by_id(org_id) user = org.get_user(user_id) key_archive = user.build_key_archive() response = flask.Response(response=key_archive, mimetype='application/octet-stream') response.headers.add('Content-Disposition', 'attachment; filename="%s.tar"' % user.name) return response
def _get_onc_archive(org_id, user_id): org = organization.get_by_id(org_id) usr = org.get_user(user_id) onc_conf = usr.build_onc() response = flask.Response(response=onc_conf, mimetype='application/x-onc') response.headers.add('Content-Disposition', 'attachment; filename="%s_%s.onc"' % (org.name, usr.name)) return (usr, response)
def user_key_link_get(org_id, user_id): org = organization.get_by_id(org_id) usr = org.get_user(user_id) usr.audit_event( "user_profile", "User temporary profile links created from web console", remote_addr=utils.get_remote_addr() ) return utils.jsonify(org.create_user_key_link(user_id))
def user_key_pin_put(key_id): doc = _find_doc({ 'key_id': key_id, }) if not doc: return flask.abort(404) if settings.app.demo_mode: return utils.demo_blocked() org = organization.get_by_id(doc['org_id']) usr = org.get_user(doc['user_id']) if usr.disabled: return flask.abort(403) if RADIUS_AUTH in usr.auth_type: return utils.jsonify({ 'error': PIN_RADIUS, 'error_msg': PIN_RADIUS_MSG, }, 400) current_pin = utils.filter_str( flask.request.json.get('current_pin')) or None pin = utils.filter_str(flask.request.json.get('pin')) or None if pin: if not pin.isdigit(): return utils.jsonify({ 'error': PIN_NOT_DIGITS, 'error_msg': PIN_NOT_DIGITS_MSG, }, 400) if len(pin) < settings.user.pin_min_length: return utils.jsonify({ 'error': PIN_TOO_SHORT, 'error_msg': PIN_TOO_SHORT_MSG, }, 400) if usr.pin and not usr.check_pin(current_pin): return utils.jsonify({ 'error': PIN_INVALID, 'error_msg': PIN_INVALID_MSG, }, 400) if usr.set_pin(pin): usr.audit_event('user_updated', 'User pin changed with temporary profile link', remote_addr=utils.get_remote_addr(), ) usr.commit() event.Event(type=USERS_UPDATED, resource_id=org.id) return utils.jsonify({})
def user_post(org_id): if settings.app.demo_mode: return utils.demo_blocked() org = organization.get_by_id(org_id) users = [] if isinstance(flask.request.json, list): users_data = flask.request.json else: users_data = [flask.request.json] try: for user_data in users_data: name = utils.filter_str(user_data['name']) email = utils.filter_str(user_data.get('email')) disabled = user_data.get('disabled') network_links = user_data.get('network_links') bypass_secondary = user_data.get('bypass_secondary') dns_servers = user_data.get('dns_servers') or None dns_suffix = utils.filter_str(user_data.get('dns_suffix')) or None user = org.new_user(type=CERT_CLIENT, name=name, email=email, disabled=disabled, bypass_secondary=bypass_secondary, dns_servers=dns_servers, dns_suffix=dns_suffix) user.audit_event('user_created', 'User created from web console', remote_addr=utils.get_remote_addr(), ) if network_links: for network_link in network_links: try: user.add_network_link(network_link) except (ipaddress.AddressValueError, ValueError): return _network_link_invalid() except ServerOnlineError: return utils.jsonify({ 'error': NETWORK_LINK_NOT_OFFLINE, 'error_msg': NETWORK_LINK_NOT_OFFLINE_MSG, }, 400) users.append(user.dict()) finally: event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) if isinstance(flask.request.json, list): logger.LogEntry(message='Created %s new users.' % len( flask.request.json)) return utils.jsonify(users) else: logger.LogEntry(message='Created new user "%s".' % users[0]['name']) return utils.jsonify(users[0])
def user_linked_key_page_get(short_code): doc = _find_doc({ 'short_id': short_code, }, one_time=True) if not doc: return flask.abort(404) org = organization.get_by_id(doc['org_id']) user = org.get_user(id=doc['user_id']) user.audit_event('user_profile', 'User temporary profile link viewed', remote_addr=utils.get_remote_addr(), ) if settings.local.sub_active and settings.app.theme == 'dark': view_name = KEY_VIEW_DARK_NAME else: view_name = KEY_VIEW_NAME key_page = static.StaticFile(settings.conf.www_path, view_name, cache=False).data key_page = key_page.replace('<%= user_name %>', '%s - %s' % ( org.name, user.name)) key_page = key_page.replace('<%= user_key_tar_url %>', '/key/%s.tar' % ( doc['key_id'])) key_page = key_page.replace('<%= user_key_zip_url %>', '/key/%s.zip' % ( doc['key_id'])) if org.otp_auth: key_page = key_page.replace('<%= user_otp_key %>', user.otp_secret) key_page = key_page.replace('<%= user_otp_url %>', 'otpauth://totp/%s@%s?secret=%s' % ( user.name, org.name, user.otp_secret)) else: key_page = key_page.replace('<%= user_otp_key %>', '') key_page = key_page.replace('<%= user_otp_url %>', '') key_page = key_page.replace('<%= short_id %>', doc['short_id']) conf_links = '' if settings.local.sub_active: conf_links += '<a class="btn btn-success" ' + \ 'title="Download Chromebook Profiles" ' + \ 'href="/key_onc/%s.zip">Download Chromebook Profiles</a><br>\n' % ( doc['key_id']) for server in org.iter_servers(): conf_links += '<a class="btn btn-sm" title="Download Profile" ' + \ 'href="/key/%s/%s.key">Download Profile (%s)</a><br>\n' % ( doc['key_id'], server.id, server.name) key_page = key_page.replace('<%= conf_links %>', conf_links) return key_page
def user_put(org_id, user_id): org = organization.get_by_id(org_id) user = org.get_user(user_id) if 'name' in flask.request.json: user.name = utils.filter_str(flask.request.json['name']) or None if 'email' in flask.request.json: user.email = utils.filter_str(flask.request.json['email']) or None disabled = flask.request.json.get('disabled') if disabled is not None: user.disabled = disabled user.commit() event.Event(type=USERS_UPDATED, resource_id=user.org.id) if disabled: if user.type == CERT_CLIENT: logger.LogEntry(message='Disabled user "%s".' % user.name) for svr in org.iter_servers(fields=server.dict_fields + \ ['hosts', 'links', 'replica_count', 'tls_auth_key', 'ca_certificate']): for instance in svr.instances: for client in instance['clients']: if client['id'] == user_id: svr.restart() break if user_id in instance['clients']: svr.restart() elif disabled == False and user.type == CERT_CLIENT: logger.LogEntry(message='Enabled user "%s".' % user.name) send_key_email = flask.request.json.get('send_key_email') if send_key_email and user.email: try: user.send_key_email(send_key_email) except EmailNotConfiguredError: return utils.jsonify({ 'error': EMAIL_NOT_CONFIGURED, 'error_msg': EMAIL_NOT_CONFIGURED_MSG, }, 400) except EmailFromInvalid: return utils.jsonify({ 'error': EMAIL_FROM_INVALID, 'error_msg': EMAIL_FROM_INVALID_MSG, }, 400) except EmailAuthInvalid: return utils.jsonify({ 'error': EMAIL_AUTH_INVALID, 'error_msg': EMAIL_AUTH_INVALID_MSG, }, 400) return utils.jsonify(user.dict())
def get_by_id(id, fields=None, load_org=False): from pritunl import organization user = User(id=id, fields=fields) if not user: return None if load_org: user.org = organization.get_by_id(user.id) return user
def user_linked_key_conf_get(org_id, user_id, server_id): org = organization.get_by_id(org_id) usr = org.get_user(user_id) key_conf = usr.build_key_conf(server_id) usr.audit_event("user_profile", "User key profile downloaded from web console", remote_addr=utils.get_remote_addr()) response = flask.Response(response=key_conf["conf"], mimetype="application/ovpn") response.headers.add("Content-Disposition", 'attachment; filename="%s"' % key_conf["name"]) return response
def user_linked_key_page_get(short_code): utils.rand_sleep() collection = mongo.get_collection('users_key_link') doc = collection.find_one({ 'short_id': short_code, }) if not doc: time.sleep(settings.app.rate_limit_sleep) return flask.abort(404) org = organization.get_by_id(doc['org_id']) user = org.get_user(id=doc['user_id']) if settings.local.sub_active and settings.app.theme == 'dark': view_name = KEY_VIEW_DARK_NAME else: view_name = KEY_VIEW_NAME key_page = static.StaticFile(settings.conf.www_path, view_name, cache=False).data key_page = key_page.replace('<%= user_name %>', '%s - %s' % ( org.name, user.name)) key_page = key_page.replace('<%= user_key_url %>', '/key/%s.tar' % ( doc['key_id'])) if org.otp_auth: key_page = key_page.replace('<%= user_otp_key %>', user.otp_secret) key_page = key_page.replace('<%= user_otp_url %>', 'otpauth://totp/%s@%s?secret=%s' % ( user.name, org.name, user.otp_secret)) else: key_page = key_page.replace('<%= user_otp_key %>', '') key_page = key_page.replace('<%= user_otp_url %>', '') key_page = key_page.replace('<%= short_id %>', doc['short_id']) conf_links = '' if settings.local.sub_active and settings.local.sub_plan == 'enterprise': conf_links += '<a class="btn btn-success" ' + \ 'style="margin-top: 7px;" ' + \ 'title="Download Chromebook Keys" ' + \ 'href="/key_onc/%s.zip">Download Chromebook Keys</a><br>\n' % ( doc['key_id']) for server in org.iter_servers(): conf_links += '<a class="btn btn-sm" title="Download Key" ' + \ 'href="/key/%s/%s.key">Download Key (%s)</a><br>\n' % ( doc['key_id'], server.id, server.name) key_page = key_page.replace('<%= conf_links %>', conf_links) return key_page
def org_delete(org_id): org = organization.get_by_id(org_id) name = org.name server_ids = org.remove() logger.LogEntry(message='Deleted organization "%s".' % name) for server_id in server_ids: event.Event(type=SERVER_ORGS_UPDATED, resource_id=server_id) event.Event(type=SERVERS_UPDATED) event.Event(type=ORGS_UPDATED) return utils.jsonify({})
def user_audit_get(org_id, user_id): if settings.app.demo_mode: resp = utils.demo_get_cache() if resp: return utils.jsonify(resp) org = organization.get_by_id(org_id) user = org.get_user(user_id) resp = user.get_audit_events() if settings.app.demo_mode: utils.demo_set_cache(resp) return utils.jsonify(resp)
def key_sync_get(org_id, user_id, server_id): org = organization.get_by_id(org_id) user = org.get_user(id=user_id) sync_key = flask.request.json['sync_key'] key_hash = flask.request.json['key_hash'] if sync_key != user.sync_key: raise flask.abort(401) key_conf = user.sync_conf(server_id, key_hash) if key_conf: return key_conf['conf'] return ''
def user_delete(org_id, user_id): org = organization.get_by_id(org_id) user = org.get_user(user_id) name = user.name user.remove() event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) user.disconnect() logger.LogEntry(message='Deleted user "%s".' % name) return utils.jsonify({})
def key_sync_get(org_id, user_id, server_id, key_hash): org_id = org_id user_id = user_id server_id = server_id key_hash = key_hash[:256] remote_addr = utils.get_remote_addr() if not settings.user.conf_sync: return utils.jsonify({}) if not settings.local.sub_active: return utils.jsonify({}, status_code=480) utils.rand_sleep() auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Missing auth header', ) return flask.abort(406) auth_token = auth_token[:256] auth_timestamp = auth_timestamp[:256] auth_nonce = auth_nonce[:32] auth_signature = auth_signature[:512] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Expired auth timestamp', ) return flask.abort(408) except ValueError: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Invalid auth timestamp', ) return flask.abort(405) org = organization.get_by_id(org_id) if not org: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Organization not found', ) return flask.abort(404) usr = org.get_user(id=user_id) if not usr: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='User not found', ) return flask.abort(404) elif not usr.sync_secret: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User missing sync secret', ) return flask.abort(410) if auth_token != usr.sync_token: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Sync token mismatch', ) return flask.abort(410) if usr.disabled: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User disabled', ) return flask.abort(403) auth_string = '&'.join([ usr.sync_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path ]) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Auth string len limit exceeded', ) return flask.abort(413) auth_test_signature = base64.b64encode( hmac.new(usr.sync_secret.encode(), auth_string, hashlib.sha512).digest()) if not utils.const_compare(auth_signature, auth_test_signature): journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Sync signature mismatch', ) return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Duplicate key', ) return flask.abort(409) key_conf = usr.sync_conf(server_id, key_hash) if key_conf: usr.audit_event( 'user_profile', 'User profile synced from pritunl client', remote_addr=remote_addr, ) journal.entry( journal.USER_SYNC_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User profile synced from pritunl client', ) sync_signature = base64.b64encode( hmac.new(usr.sync_secret.encode(), key_conf['conf'], hashlib.sha512).digest()) return utils.jsonify({ 'signature': sync_signature, 'conf': key_conf['conf'], }) return utils.jsonify({})
def _auth_radius(username, password): sso_mode = settings.app.sso valid, org_names, groups = sso.verify_radius(username, password) if not valid: return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) org_id = settings.app.sso_org if org_names: for org_name in org_names: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id break valid, org_id_new, groups2 = sso.plugin_sso_authenticate( sso_type='radius', user_name=username, user_email=None, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: logger.error( 'Radius plugin authentication not valid', 'sso', username=username, ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) groups = ((groups or set()) | (groups2 or set())) or None if DUO_AUTH in sso_mode: try: duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type='Key', ) valid = duo_auth.authenticate() except InvalidUser: logger.error( 'Duo authentication username not valid', 'sso', username=username, ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) if valid: valid, org_id_new, groups2 = sso.plugin_sso_authenticate( sso_type='duo', user_name=username, user_email=None, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: logger.error( 'Duo plugin authentication not valid', 'sso', username=username, ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) groups = ((groups or set()) | (groups2 or set())) or None else: logger.error( 'Duo authentication not valid', 'sso', username=username, ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) groups = ((groups or set()) | (groups2 or set())) or None org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, type=CERT_CLIENT, auth_type=sso_mode, groups=list(groups) if groups else None) usr.audit_event( 'user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr(), ) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return utils.jsonify( { 'error': AUTH_DISABLED, 'error_msg': AUTH_DISABLED_MSG, }, 403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.set_pin(None) usr.commit(('auth_type', 'pin')) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.jsonify( { 'redirect': utils.get_url_root() + key_link['view_url'], }, 202)
def key_wg_post(org_id, user_id, server_id): org_id = org_id user_id = user_id server_id = server_id remote_addr = utils.get_remote_addr() if not settings.user.conf_sync: return utils.jsonify({}) if not settings.local.sub_active: return utils.jsonify({}, status_code=480) auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='Missing auth header', ) return flask.abort(406) auth_token = auth_token[:256] auth_timestamp = auth_timestamp[:256] auth_nonce = auth_nonce[:32] auth_signature = auth_signature[:512] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='Expired auth timestamp', ) return flask.abort(408) except ValueError: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='Invalid auth timestamp', ) return flask.abort(405) org = organization.get_by_id(org_id) if not org: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='Organization not found', ) return flask.abort(404) usr = org.get_user(id=user_id) if not usr: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='User not found', ) return flask.abort(404) elif not usr.sync_secret: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User missing sync secret', ) return flask.abort(410) if auth_token != usr.sync_token: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Sync token mismatch', ) return flask.abort(411) if usr.disabled: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User disabled', ) return flask.abort(403) cipher_data64 = flask.request.json.get('data') box_nonce64 = flask.request.json.get('nonce') public_key64 = flask.request.json.get('public_key') signature64 = flask.request.json.get('signature') auth_string = '&'.join([ usr.sync_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path, cipher_data64, box_nonce64, public_key64, signature64 ]) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Auth string len limit exceeded', ) return flask.abort(414) auth_test_signature = base64.b64encode( hmac.new(usr.sync_secret.encode(), auth_string, hashlib.sha512).digest()) if not utils.const_compare(auth_signature, auth_test_signature): journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Auth signature mismatch', ) return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Duplicate nonce', ) return flask.abort(409) data_hash = hashlib.sha512('&'.join( [cipher_data64, box_nonce64, public_key64])).digest() try: usr.verify_sig( data_hash, base64.b64decode(signature64), ) except InvalidSignature: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Invalid rsa signature', ) return flask.abort(412) svr = usr.get_server(server_id) sender_pub_key = nacl.public.PublicKey(base64.b64decode(public_key64)) box_nonce = base64.b64decode(box_nonce64) priv_key = nacl.public.PrivateKey( base64.b64decode(svr.auth_box_private_key)) cipher_data = base64.b64decode(cipher_data64) nacl_box = nacl.public.Box(priv_key, sender_pub_key) plaintext = nacl_box.decrypt(cipher_data, box_nonce).decode('utf-8') try: nonces_collection.insert({ 'token': auth_token, 'nonce': box_nonce64, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Duplicate secondary nonce', ) return flask.abort(415) key_data = json.loads(plaintext) client_platform = key_data['platform'] client_device_id = key_data['device_id'] client_device_name = key_data['device_name'] client_mac_addr = key_data['mac_addr'] client_auth_token = key_data['token'] client_auth_nonce = key_data['nonce'] client_auth_password = key_data['password'] client_auth_timestamp = key_data['timestamp'] client_wg_public_key = key_data['wg_public_key'] if len(client_wg_public_key) < 32: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Public key too short', ) return flask.abort(416) try: client_wg_public_key = base64.b64decode(client_wg_public_key) if len(client_wg_public_key) != 32: raise ValueError('Invalid length') client_wg_public_key = base64.b64encode(client_wg_public_key) except: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Public key invalid', ) return flask.abort(417) wg_keys_collection = mongo.get_collection('wg_keys') try: wg_keys_collection.insert({ '_id': client_wg_public_key, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Duplicate wg public key', ) return flask.abort(413) instance = server.get_instance(server_id) if not instance or instance.state != 'running': return flask.abort(429) if not instance.server.wg: return flask.abort(429) clients = instance.instance_com.clients event = threading.Event() send_data = { 'allow': None, 'configuration': None, 'reason': None, } def callback(allow, data): send_data['allow'] = allow if allow: send_data['configuration'] = data else: send_data['reason'] = data event.set() clients.connect_wg( user=usr, org=org, wg_public_key=client_wg_public_key, auth_password=client_auth_password, auth_token=client_auth_token, auth_nonce=client_auth_nonce, auth_timestamp=client_auth_timestamp, platform=client_platform, device_id=client_device_id, device_name=client_device_name, mac_addr=client_mac_addr, remote_ip=remote_addr, connect_callback=callback, ) event.wait() send_nonce = nacl.utils.random(nacl.public.Box.NONCE_SIZE) nacl_box = nacl.public.Box(priv_key, sender_pub_key) send_cipher_data = nacl_box.encrypt(json.dumps(send_data), send_nonce) send_cipher_data = send_cipher_data[nacl.public.Box.NONCE_SIZE:] send_nonce64 = base64.b64encode(send_nonce) send_cipher_data64 = base64.b64encode(send_cipher_data) usr.audit_event( 'user_profile', 'User retrieved wg public key from pritunl client', remote_addr=remote_addr, ) journal.entry( journal.USER_WG_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User retrieved wg public key from pritunl client', ) sync_signature = base64.b64encode( hmac.new(usr.sync_secret.encode(), send_cipher_data64 + '&' + send_nonce64, hashlib.sha512).digest()) return utils.jsonify({ 'data': send_cipher_data64, 'nonce': send_nonce64, 'signature': sync_signature, })
def _auth_plugin(username, password, remote_addr): if not settings.local.sub_plan or \ 'enterprise' not in settings.local.sub_plan: journal.entry( journal.ADMIN_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.ADMIN_AUTH_REASON_INVALID_USERNAME, reason_long='Invalid username', ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) has_plugin, valid, org_id, groups = sso.plugin_login_authenticate( user_name=username, password=password, remote_ip=remote_addr, ) if not has_plugin: journal.entry( journal.ADMIN_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.ADMIN_AUTH_REASON_INVALID_USERNAME, reason_long='Invalid username', ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) if not valid: journal.entry( journal.SSO_AUTH_REASON_PLUGIN_FAILED, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Plugin authentication failed', ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) if not org_id: logger.error( 'Login plugin did not return valid organization name', 'auth', org_name=org_id, user_name=username, ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) org = organization.get_by_id(org_id) if not org: logger.error( 'Organization for sso does not exist', 'auth', org_id=org_id, ) return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, type=CERT_CLIENT, auth_type=PLUGIN_AUTH, groups=list(groups) if groups else None) usr.audit_event( 'user_created', 'User created with plugin authentication', remote_addr=utils.get_remote_addr(), ) journal.entry( journal.USER_CREATE, usr.journal_data, event_long='User created with plugin authentication', remote_address=remote_addr, ) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return utils.jsonify( { 'error': AUTH_DISABLED, 'error_msg': AUTH_DISABLED_MSG, }, 403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != PLUGIN_AUTH: usr.auth_type = PLUGIN_AUTH usr.set_pin(None) usr.commit(('auth_type', 'pin')) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from plugin authentication', remote_addr=utils.get_remote_addr(), ) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, event_long='User profile viewed from plugin authentication', remote_address=remote_addr, ) return utils.jsonify( { 'redirect': utils.get_url_root() + key_link['view_url'], }, 202)
def user_put(org_id, user_id): if settings.app.demo_mode: return utils.demo_blocked() org = organization.get_by_id(org_id) user = org.get_user(user_id) reset_user = False port_forwarding_event = False if 'name' in flask.request.json: name = utils.filter_str(flask.request.json['name']) or 'undefined' if name != user.name: user.audit_event( 'user_updated', 'User name changed', remote_addr=utils.get_remote_addr(), ) user.name = name if 'email' in flask.request.json: email = utils.filter_str(flask.request.json['email']) or None if email != user.email: user.audit_event( 'user_updated', 'User email changed', remote_addr=utils.get_remote_addr(), ) user.email = email if 'groups' in flask.request.json: groups = flask.request.json['groups'] or [] for i, group in enumerate(groups): groups[i] = utils.filter_str(group) groups = set(groups) if groups != set(user.groups or []): user.audit_event( 'user_updated', 'User groups changed', remote_addr=utils.get_remote_addr(), ) user.groups = list(groups) if 'pin' in flask.request.json: pin = flask.request.json['pin'] or None if pin is not True: if pin: if settings.user.pin_mode == PIN_DISABLED: return utils.jsonify( { 'error': PIN_IS_DISABLED, 'error_msg': PIN_IS_DISABLED_MSG, }, 400) if RADIUS_AUTH in user.auth_type: return utils.jsonify( { 'error': PIN_RADIUS, 'error_msg': PIN_RADIUS_MSG, }, 400) if not pin.isdigit(): return utils.jsonify( { 'error': PIN_NOT_DIGITS, 'error_msg': PIN_NOT_DIGITS_MSG, }, 400) if len(pin) < settings.user.pin_min_length: return utils.jsonify( { 'error': PIN_TOO_SHORT, 'error_msg': PIN_TOO_SHORT_MSG, }, 400) if user.set_pin(pin): user.audit_event( 'user_updated', 'User pin changed', remote_addr=utils.get_remote_addr(), ) if 'network_links' in flask.request.json: network_links_cur = set(user.get_network_links()) network_links_new = set() for network_link in flask.request.json['network_links'] or []: try: network_link = str(ipaddress.IPNetwork(network_link)) except (ipaddress.AddressValueError, ValueError): return _network_link_invalid() network_links_new.add(network_link) network_links_add = network_links_new - network_links_cur network_links_rem = network_links_cur - network_links_new if len(network_links_add) or len(network_links_rem): reset_user = True user.audit_event( 'user_updated', 'User network links updated', remote_addr=utils.get_remote_addr(), ) try: for network_link in network_links_add: user.add_network_link(network_link) except ServerOnlineError: return utils.jsonify( { 'error': NETWORK_LINK_NOT_OFFLINE, 'error_msg': NETWORK_LINK_NOT_OFFLINE_MSG, }, 400) for network_link in network_links_rem: user.remove_network_link(network_link) if 'port_forwarding' in flask.request.json: port_forwarding = [] for data in flask.request.json['port_forwarding'] or []: port_forwarding.append({ 'protocol': utils.filter_str(data.get('protocol')), 'port': utils.filter_str(data.get('port')), 'dport': utils.filter_str(data.get('dport')), }) if port_forwarding != user.port_forwarding: port_forwarding_event = True user.audit_event( 'user_updated', 'User port forwarding changed', remote_addr=utils.get_remote_addr(), ) user.port_forwarding = port_forwarding disabled = True if flask.request.json.get('disabled') else False if disabled != user.disabled: user.audit_event( 'user_updated', 'User %s' % ('disabled' if disabled else 'enabled'), remote_addr=utils.get_remote_addr(), ) if disabled: reset_user = True user.disabled = disabled user.bypass_secondary = True if flask.request.json.get( 'bypass_secondary') else False user.client_to_client = True if flask.request.json.get( 'client_to_client') else False if 'dns_servers' in flask.request.json: dns_servers = flask.request.json['dns_servers'] or None if user.dns_servers != dns_servers: user.audit_event( 'user_updated', 'User dns servers changed', remote_addr=utils.get_remote_addr(), ) reset_user = True user.dns_servers = dns_servers if 'dns_suffix' in flask.request.json: dns_suffix = utils.filter_str(flask.request.json['dns_suffix']) or None if user.dns_suffix != dns_suffix: user.audit_event( 'user_updated', 'User dns suffix changed', remote_addr=utils.get_remote_addr(), ) reset_user = True user.dns_suffix = dns_suffix user.commit() event.Event(type=USERS_UPDATED, resource_id=user.org.id) if port_forwarding_event: messenger.publish('port_forwarding', { 'org_id': org.id, 'user_id': user.id, }) if reset_user: user.disconnect() send_key_email = flask.request.json.get('send_key_email') if send_key_email and user.email: user.audit_event( 'user_emailed', 'User key email sent to "%s"' % user.email, remote_addr=utils.get_remote_addr(), ) try: user.send_key_email(utils.get_url_root()) except EmailNotConfiguredError: return utils.jsonify( { 'error': EMAIL_NOT_CONFIGURED, 'error_msg': EMAIL_NOT_CONFIGURED_MSG, }, 400) except EmailFromInvalid: return utils.jsonify( { 'error': EMAIL_FROM_INVALID, 'error_msg': EMAIL_FROM_INVALID_MSG, }, 400) except EmailAuthInvalid: return utils.jsonify( { 'error': EMAIL_AUTH_INVALID, 'error_msg': EMAIL_AUTH_INVALID_MSG, }, 400) return utils.jsonify(user.dict())
def _validate_user(username, email, sso_mode, org_id, groups, remote_addr, http_redirect=False, yubico_id=None): usr = user.find_user_auth(name=username, auth_type=sso_mode) if not usr: org = organization.get_by_id(org_id) if not org: logger.error( 'Organization for sso does not exist', 'sso', org_id=org_id, ) return flask.abort(405) usr = org.find_user(name=username) else: if usr.org_id != org_id: logger.info( 'User organization changed, moving user', 'sso', user_name=username, user_email=email, remote_ip=remote_addr, cur_org_id=usr.org_id, new_org_id=org_id, ) org = organization.get_by_id(org_id) if not org: logger.error( 'Organization for sso does not exist', 'sso', org_id=org_id, ) return flask.abort(405) usr.remove() old_org_id = usr.org_id usr = org.new_user( name=usr.name, email=usr.email, type=usr.type, groups=usr.groups, auth_type=usr.auth_type, yubico_id=usr.yubico_id, disabled=usr.disabled, bypass_secondary=usr.bypass_secondary, client_to_client=usr.client_to_client, dns_servers=usr.dns_servers, dns_suffix=usr.dns_suffix, port_forwarding=usr.port_forwarding, ) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=old_org_id) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) org = usr.org if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, yubico_id=yubico_id, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=remote_addr) journal.entry( journal.USER_CREATE, usr.journal_data, event_long='User created with single sign-on', remote_address=remote_addr, ) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if yubico_id and usr.yubico_id and yubico_id != usr.yubico_id: journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_INVALID_YUBIKEY, reason_long='Invalid username', ) return utils.jsonify( { 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) if usr.disabled: return flask.abort(403) changed = False if yubico_id and not usr.yubico_id: changed = True usr.yubico_id = yubico_id usr.commit('yubico_id') if groups and groups - set(usr.groups or []): changed = True usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: changed = True usr.auth_type = sso_mode usr.commit('auth_type') if changed: event.Event(type=USERS_UPDATED, resource_id=org.id) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=remote_addr, ) journal.entry( journal.SSO_AUTH_SUCCESS, usr.journal_data, key_id_hash=hashlib.md5(key_link['id']).hexdigest(), remote_address=remote_addr, ) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User profile viewed from single sign-on', ) if http_redirect: return utils.redirect(utils.get_url_root() + key_link['view_url']) else: return utils.jsonify( { 'redirect': utils.get_url_root() + key_link['view_url'], }, 200)
def sso_duo_post(): sso_mode = settings.app.sso token = utils.filter_str(flask.request.json.get('token')) or None passcode = utils.filter_str(flask.request.json.get('passcode')) or '' if sso_mode not in (DUO_AUTH, GOOGLE_DUO_AUTH, SLACK_DUO_AUTH, SAML_DUO_AUTH, SAML_OKTA_DUO_AUTH, SAML_ONELOGIN_DUO_AUTH, RADIUS_DUO_AUTH): return flask.abort(404) if not token: return utils.jsonify( { 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_one({ '_id': token, }) if not doc or doc['_id'] != token or doc['type'] != DUO_AUTH: return utils.jsonify( { 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'] email = doc['email'] org_id = doc['org_id'] groups = set(doc['groups'] or []) if settings.app.sso_duo_mode == 'passcode': duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type='Key', passcode=passcode, ) valid = duo_auth.authenticate() if not valid: logger.error( 'Duo authentication not valid', 'sso', username=username, ) return utils.jsonify( { 'error': PASSCODE_INVALID, 'error_msg': PASSCODE_INVALID_MSG, }, 401) else: duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type='Key', ) valid = duo_auth.authenticate() if not valid: logger.error( 'Duo authentication not valid', 'sso', username=username, ) return utils.jsonify( { 'error': DUO_FAILED, 'error_msg': DUO_FAILED_MSG, }, 401) valid, org_id_new, groups2 = sso.plugin_sso_authenticate( sso_type='duo', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: logger.error( 'Duo plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) groups = groups | set(groups2 or []) usr = user.find_user_auth(name=username, auth_type=sso_mode) if not usr: org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) else: org = usr.org if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.jsonify( { 'redirect': utils.get_url_root() + key_link['view_url'], }, 200)
def key_sync_get(org_id, user_id, server_id, key_hash): if not settings.user.conf_sync: return utils.jsonify({}) if not settings.local.sub_active: return utils.jsonify({}, status_code=480) utils.rand_sleep() auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: return flask.abort(406) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: return flask.abort(408) except ValueError: return flask.abort(405) org = organization.get_by_id(org_id) if not org: return flask.abort(404) usr = org.get_user(id=user_id) if not usr: return flask.abort(404) elif not usr.sync_secret: return flask.abort(410) if auth_token != usr.sync_token: return flask.abort(410) if usr.disabled: return flask.abort(403) auth_string = '&'.join([ usr.sync_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path ]) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: return flask.abort(413) auth_test_signature = base64.b64encode( hmac.new(usr.sync_secret.encode(), auth_string, hashlib.sha512).digest()) if not utils.const_compare(auth_signature, auth_test_signature): return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: return flask.abort(409) key_conf = usr.sync_conf(server_id, key_hash) if key_conf: usr.audit_event( 'user_profile', 'User profile synced from pritunl client', remote_addr=utils.get_remote_addr(), ) sync_signature = base64.b64encode( hmac.new(usr.sync_secret.encode(), key_conf['conf'], hashlib.sha512).digest()) return utils.jsonify({ 'signature': sync_signature, 'conf': key_conf['conf'], }) return utils.jsonify({})
def user_key_link_get(org_id, user_id): org = organization.get_by_id(org_id) return utils.jsonify(org.create_user_key_link(user_id))
def user_key_pin_put(key_id): if settings.app.demo_mode: return utils.demo_blocked() doc = _find_doc({ 'key_id': key_id, }) if not doc: return flask.abort(404) if settings.app.demo_mode: return utils.demo_blocked() if settings.user.pin_mode == PIN_DISABLED: return utils.jsonify( { 'error': PIN_IS_DISABLED, 'error_msg': PIN_IS_DISABLED_MSG, }, 400) org = organization.get_by_id(doc['org_id']) usr = org.get_user(doc['user_id']) if usr.disabled: return flask.abort(403) if RADIUS_AUTH in usr.auth_type: return utils.jsonify( { 'error': PIN_RADIUS, 'error_msg': PIN_RADIUS_MSG, }, 400) current_pin = utils.filter_str( flask.request.json.get('current_pin')) or None pin = utils.filter_str(flask.request.json.get('pin')) or None if pin: if settings.user.pin_digits_only and not pin.isdigit(): return utils.jsonify( { 'error': PIN_NOT_DIGITS, 'error_msg': PIN_NOT_DIGITS_MSG, }, 400) if len(pin) < settings.user.pin_min_length: return utils.jsonify( { 'error': PIN_TOO_SHORT, 'error_msg': PIN_TOO_SHORT_MSG, }, 400) if usr.pin: if not limiter.auth_check(usr.id): return utils.jsonify( { 'error': AUTH_TOO_MANY, 'error_msg': AUTH_TOO_MANY_MSG, }, 400) if not usr.check_pin(current_pin): return utils.jsonify( { 'error': PIN_INVALID, 'error_msg': PIN_INVALID_MSG, }, 400) if usr.set_pin(pin): usr.audit_event( 'user_updated', 'User pin changed with temporary profile link', remote_addr=utils.get_remote_addr(), ) usr.commit() event.Event(type=USERS_UPDATED, resource_id=org.id) return utils.jsonify({})
def user_linked_key_page_get(short_code): doc = _find_doc({ 'short_id': short_code, }, one_time=True, one_time_new=True) if not doc: return flask.abort(404) org = organization.get_by_id(doc['org_id']) usr = org.get_user(id=doc['user_id']) if usr.disabled: return flask.abort(403) usr.audit_event( 'user_profile', 'User temporary profile link viewed', remote_addr=utils.get_remote_addr(), ) if settings.local.sub_active and settings.app.theme == 'dark': view_name = KEY_VIEW_DARK_NAME else: view_name = KEY_VIEW_NAME if RADIUS_AUTH in usr.auth_type or \ settings.user.pin_mode == PIN_DISABLED: header_class = 'pin-disabled' else: header_class = '' key_page = static.StaticFile(settings.conf.www_path, view_name, cache=False, gzip=False).data uri_url = (utils.get_url_root() + '/ku/' + doc['short_id']).encode() if uri_url.startswith('https'): uri_url = uri_url.replace('https', 'pritunl', 1) else: uri_url = uri_url.replace('http', 'pritunl', 1) key_page = key_page.replace('<%= uri_url %>', uri_url) key_page = key_page.replace('<%= user_name %>', '%s - %s' % (org.name, usr.name)) key_page = key_page.replace('<%= user_key_tar_url %>', '/key/%s.tar' % (doc['key_id'])) key_page = key_page.replace('<%= user_key_zip_url %>', '/key/%s.zip' % (doc['key_id'])) if org.otp_auth and not usr.has_duo_passcode and not usr.has_yubikey: key_page = key_page.replace('<%= user_otp_key %>', usr.otp_secret) key_page = key_page.replace( '<%= user_otp_url %>', 'otpauth://totp/%s@%s?secret=%s' % (usr.name, org.name, usr.otp_secret)) else: key_page = key_page.replace('<%= user_otp_key %>', '') key_page = key_page.replace('<%= user_otp_url %>', '') if usr.pin: key_page = key_page.replace('<%= cur_pin_display %>', 'block') else: key_page = key_page.replace('<%= cur_pin_display %>', 'none') key_page = key_page.replace('<%= key_id %>', doc['key_id']) key_page = key_page.replace('<%= short_id %>', doc['short_id']) conf_links = '' if settings.local.sub_active: conf_links += '<a class="btn btn-success download-chrome" ' + \ 'title="Download Chrome OS Profile" ' + \ 'href="/key_onc/%s.onc">Download Chrome OS Profile</a>\n' % ( doc['key_id']) has_servers = False for server in usr.iter_servers(): has_servers = True conf_links += '<a class="btn btn-sm download-profile" ' + \ 'title="Download Profile" ' + \ 'href="/key/%s/%s.key">Download Profile (%s)</a>\n' % ( doc['key_id'], server.id, server.name) key_page = key_page.replace('<%= conf_links %>', conf_links) if not has_servers: header_class += 'no-servers' key_page = key_page.replace('<%= header_class %>', header_class) return key_page
def get_org(self, org_id, fields=None): if org_id in self.organizations: return organization.get_by_id(org_id, fields=fields)
def user_get(org_id, user_id=None, page=None): org = organization.get_by_id(org_id) if user_id: return utils.jsonify(org.get_user(user_id).dict()) page = flask.request.args.get('page', None) page = int(page) if page else page search = flask.request.args.get('search', None) limit = int(flask.request.args.get('limit', settings.user.page_count)) otp_auth = False server_count = 0 servers = [] for svr in org.iter_servers(fields=('name', 'otp_auth')): servers.append(svr) server_count += 1 if svr.otp_auth: otp_auth = True users = [] users_id = [] users_data = {} users_servers = {} fields = ( 'organization', 'organization_name', 'name', 'email', 'type', 'otp_secret', 'disabled', ) for usr in org.iter_users(page=page, search=search, search_limit=limit, fields=fields): users_id.append(usr.id) user_dict = usr.dict() user_dict['status'] = False user_dict['otp_auth'] = otp_auth users_data[usr.id] = user_dict users_servers[usr.id] = {} server_data = [] for svr in servers: data = { 'id': svr.id, 'name': svr.name, 'status': False, 'client_id': None, 'device_name': None, 'platform': None, 'real_address': None, 'virt_address': None, 'connected_since': None } server_data.append(data) users_servers[usr.id][svr.id] = data user_dict['servers'] = sorted(server_data, key=lambda x: x['name']) users.append(user_dict) clients_collection = mongo.get_collection('clients') for doc in clients_collection.find({ 'user_id': {'$in': users_id}, }): server_data = users_servers[doc['user_id']].get(doc['server_id']) if not server_data: continue users_data[doc['user_id']]['status'] = True server_data['status'] = True server_data['client_id'] = doc['_id'] server_data['device_name'] = doc['device_name'] server_data['platform'] = doc['platform'] server_data['real_address'] = doc['real_address'] server_data['virt_address'] = doc['virt_address'].split('/')[0] server_data['connected_since'] = doc['connected_since'] ip_addrs_iter = server.multi_get_ip_addr(org_id, users_id) for user_id, server_id, ip_add in ip_addrs_iter: server_data = users_servers[user_id].get(server_id) if server_data and not server_data['virt_address']: server_data['virt_address'] = ip_add if page is not None: return utils.jsonify({ 'page': page, 'page_total': org.page_total, 'server_count': server_count, 'users': users, }) elif search is not None: return utils.jsonify({ 'search': search, 'search_more': limit < org.last_search_count, 'search_limit': limit, 'search_count': org.last_search_count, 'search_time': round((time.time() - flask.g.start), 4), 'server_count': server_count, 'users': users, }) else: return utils.jsonify(users)
def assign_ip_pool_org(self, org_id): org = organization.get_by_id(org_id) network = self.server.network server_id = self.server.id org_id = org.id ip_pool_avial = True pool_end = False ip_network = ipaddress.IPv4Network(network) ip_pool = ip_network.iterhosts() ip_pool.next() try: doc = self.collection.find({ 'network': network, 'server_id': server_id, }).sort('_id', pymongo.DESCENDING)[0] if doc: last_addr = doc['_id'] for remote_ip_addr in ip_pool: if int(remote_ip_addr) == last_addr: break except IndexError: pass if mongo.has_bulk: bulk = self.collection.initialize_unordered_bulk_op() bulk_empty = True else: bulk = None bulk_empty = None for user in org.iter_users(include_pool=True): if ip_pool_avial: response = self.collection.update( { 'network': network, 'server_id': server_id, 'user_id': { '$exists': False }, }, {'$set': { 'org_id': org_id, 'user_id': user.id, }}) if response['updatedExisting']: continue ip_pool_avial = False try: remote_ip_addr = ip_pool.next() except StopIteration: pool_end = True break doc_id = int(remote_ip_addr) spec = { '_id': doc_id, } doc = { '$set': { '_id': doc_id, 'network': network, 'server_id': server_id, 'org_id': org_id, 'user_id': user.id, 'address': '%s/%s' % (remote_ip_addr, ip_network.prefixlen), } } if bulk: bulk.find(spec).upsert().update(doc) bulk_empty = False else: self.collection.update(spec, doc, upsert=True) if bulk and not bulk_empty: bulk.execute() if pool_end: logger.warning( 'Failed to assign ip addresses ' + 'to org, ip pool empty', 'server', org_id=org_id, )
def user_linked_key_conf_get(key_id, server_id): key_id = key_id[:128] server_id = server_id remote_addr = utils.get_remote_addr() doc = _find_doc({ 'key_id': key_id, }) if not doc: journal.entry( journal.USER_PROFILE_FAILURE, remote_address=remote_addr, event_long='Key ID not found', ) return flask.abort(404) if settings.user.restrict_import: return flask.abort(404) org = organization.get_by_id(doc['org_id']) if not org: journal.entry( journal.USER_PROFILE_FAILURE, remote_address=remote_addr, event_long='Organization not found', ) return flask.abort(404) usr = org.get_user(id=doc['user_id']) if not usr: journal.entry( journal.USER_PROFILE_FAILURE, remote_address=remote_addr, event_long='User not found', ) return flask.abort(404) if usr.disabled: journal.entry( journal.USER_PROFILE_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User disabled', ) return flask.abort(403) key_conf = usr.build_key_conf(server_id) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User profile downloaded with temporary profile link', ) usr.audit_event( 'user_profile', 'User profile downloaded with temporary profile link', remote_addr=remote_addr, ) response = flask.Response(response=key_conf['conf'], mimetype='application/ovpn') response.headers.add('Content-Disposition', 'attachment; filename="%s"' % key_conf['name']) return response
def sso_authenticate_post(): if settings.app.sso != DUO_AUTH or \ settings.app.sso_duo_mode == 'passcode': return flask.abort(405) username = utils.json_filter_str('username') usernames = [username] email = None if '@' in username: email = username usernames.append(username.split('@')[0]) valid = False for i, username in enumerate(usernames): try: duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type='Key', ) valid = duo_auth.authenticate() break except InvalidUser: if i == len(usernames) - 1: logger.error( 'Invalid duo username', 'sso', username=username, ) if valid: valid, org_id, groups = sso.plugin_sso_authenticate( sso_type='duo', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), ) if not valid: logger.error( 'Duo plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) groups = set(groups or []) else: logger.error( 'Duo authentication not valid', 'sso', username=username, ) return flask.abort(401) if not org_id: org_id = settings.app.sso_org usr = user.find_user_auth(name=username, auth_type=DUO_AUTH) if not usr: org = organization.get_by_id(org_id) if not org: logger.error( 'Organization for Duo sso does not exist', 'sso', org_id=org_id, ) return flask.abort(405) usr = org.find_user(name=username) else: org = usr.org if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=DUO_AUTH, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != DUO_AUTH: usr.auth_type = DUO_AUTH usr.commit('auth_type') event.Event(type=USERS_UPDATED, resource_id=org.id) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.get_url_root() + key_link['view_url']
def _auth_radius(username, password): valid, org_id = sso.verify_radius(username, password) if not valid: return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) if not org_id: org_id = settings.app.sso_org valid, org_id_new = sso.plugin_sso_authenticate( sso_type='radius', user_name=username, user_email=None, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: logger.error('Radius plugin authentication not valid', 'sso', username=username, ) return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, type=CERT_CLIENT, auth_type=RADIUS_AUTH) usr.audit_event( 'user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr(), ) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return utils.jsonify({ 'error': AUTH_DISABLED, 'error_msg': AUTH_DISABLED_MSG, }, 403) if usr.auth_type != RADIUS_AUTH: usr.auth_type = RADIUS_AUTH usr.set_pin(None) usr.commit(('auth_type', 'pin')) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event('user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.jsonify({ 'redirect': utils.get_url_root() + key_link['view_url'], }, 202)
def user_post(org_id): if settings.app.demo_mode: return utils.demo_blocked() org = organization.get_by_id(org_id) users = [] if isinstance(flask.request.json, list): users_data = flask.request.json else: users_data = [flask.request.json] try: for user_data in users_data: name = utils.filter_str(user_data['name']) email = utils.filter_str(user_data.get('email')) pin = utils.filter_str(user_data.get('pin')) or None disabled = user_data.get('disabled') network_links = user_data.get('network_links') bypass_secondary = user_data.get('bypass_secondary') dns_servers = user_data.get('dns_servers') or None dns_suffix = utils.filter_str(user_data.get('dns_suffix')) or None port_forwarding_in = user_data.get('port_forwarding') port_forwarding = [] if pin: if not pin.isdigit(): return utils.jsonify( { 'error': PIN_NOT_DIGITS, 'error_msg': PIN_NOT_DIGITS_MSG, }, 400) if len(pin) < settings.user.pin_min_length: return utils.jsonify( { 'error': PIN_TOO_SHORT, 'error_msg': PIN_TOO_SHORT_MSG, }, 400) pin = auth.generate_hash_pin_v2(pin) if port_forwarding_in: for data in port_forwarding_in: port_forwarding.append({ 'protocol': utils.filter_str(data.get('protocol')), 'port': utils.filter_str(data.get('port')), 'dport': utils.filter_str(data.get('dport')), }) user = org.new_user(type=CERT_CLIENT, name=name, email=email, pin=pin, disabled=disabled, bypass_secondary=bypass_secondary, dns_servers=dns_servers, dns_suffix=dns_suffix, port_forwarding=port_forwarding) user.audit_event( 'user_created', 'User created from web console', remote_addr=utils.get_remote_addr(), ) if network_links: for network_link in network_links: try: user.add_network_link(network_link) except (ipaddress.AddressValueError, ValueError): return _network_link_invalid() except ServerOnlineError: return utils.jsonify( { 'error': NETWORK_LINK_NOT_OFFLINE, 'error_msg': NETWORK_LINK_NOT_OFFLINE_MSG, }, 400) users.append(user.dict()) finally: event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) if isinstance(flask.request.json, list): logger.LogEntry(message='Created %s new users.' % len(flask.request.json)) return utils.jsonify(users) else: logger.LogEntry(message='Created new user "%s".' % users[0]['name']) return utils.jsonify(users[0])
def sso_yubico_post(): sso_mode = settings.app.sso token = utils.filter_str(flask.request.json.get('token')) or None key = utils.filter_str(flask.request.json.get('key')) or None if sso_mode not in (GOOGLE_YUBICO_AUTH, SLACK_YUBICO_AUTH, SAML_YUBICO_AUTH, SAML_OKTA_YUBICO_AUTH, SAML_ONELOGIN_YUBICO_AUTH): return flask.abort(404) if not token or not key: return utils.jsonify( { 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_one({ '_id': token, }) if not doc or doc['_id'] != token or doc['type'] != YUBICO_AUTH: return utils.jsonify( { 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'] email = doc['email'] org_id = doc['org_id'] groups = set(doc['groups'] or []) valid, yubico_id = sso.auth_yubico(key) if not valid: return utils.jsonify( { 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) usr = user.find_user_auth(name=username, auth_type=sso_mode) if not usr: org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) else: org = usr.org if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, yubico_id=yubico_id, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if yubico_id != usr.yubico_id: return utils.jsonify( { 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.jsonify( { 'redirect': utils.get_url_root() + key_link['view_url'], }, 200)
def user_get(org_id, user_id=None, page=None): if settings.app.demo_mode and user_id: resp = utils.demo_get_cache() if resp: return utils.jsonify(resp) org = organization.get_by_id(org_id) if not org: return flask.abort(404) if user_id: resp = org.get_user(user_id).dict() if settings.app.demo_mode: utils.demo_set_cache(resp) return utils.jsonify(resp) page = flask.request.args.get('page', page) page = int(page) if page else page search = flask.request.args.get('search', None) limit = int(flask.request.args.get('limit', settings.user.page_count)) otp_auth = False dns_mapping = False server_count = 0 servers = [] if settings.app.demo_mode: resp = utils.demo_get_cache(page, search, limit) if resp: return utils.jsonify(resp) for svr in org.iter_servers(fields=('name', 'otp_auth', 'dns_mapping', 'groups')): servers.append(svr) server_count += 1 if svr.otp_auth: otp_auth = True if svr.dns_mapping: dns_mapping = True users = [] users_id = [] users_data = {} users_servers = {} fields = ( 'organization', 'organization_name', 'name', 'email', 'groups', 'pin', 'type', 'auth_type', 'otp_secret', 'disabled', 'bypass_secondary', 'client_to_client', 'dns_servers', 'dns_suffix', 'port_forwarding', ) for usr in org.iter_users(page=page, search=search, search_limit=limit, fields=fields): users_id.append(usr.id) user_dict = usr.dict() user_dict['gravatar'] = settings.user.gravatar user_dict['audit'] = settings.app.auditing == ALL user_dict['status'] = False user_dict['sso'] = settings.app.sso if otp_auth and not usr.has_duo_passcode and not usr.has_yubikey: user_dict['otp_auth'] = True else: user_dict['otp_auth'] = False if dns_mapping: user_dict['dns_mapping'] = ( '%s.%s.vpn' % (str(usr.name).split('@')[0], org.name)).lower() else: user_dict['dns_mapping'] = None user_dict['network_links'] = [] users_data[usr.id] = user_dict users_servers[usr.id] = {} server_data = [] for svr in servers: if not svr.check_groups(usr.groups): continue data = { 'id': svr.id, 'name': svr.name, 'status': False, 'server_id': svr.id, 'device_name': None, 'platform': None, 'real_address': None, 'virt_address': None, 'virt_address6': None, 'connected_since': None, } server_data.append(data) users_servers[usr.id][svr.id] = data user_dict['servers'] = sorted(server_data, key=lambda x: x['name']) users.append(user_dict) clients_collection = mongo.get_collection('clients') for doc in clients_collection.find({ 'user_id': { '$in': users_id }, }): server_data = users_servers[doc['user_id']].get(doc['server_id']) if not server_data: continue users_data[doc['user_id']]['status'] = True if server_data['status']: server_data = { 'name': server_data['name'], } append = True else: append = False virt_address6 = doc.get('virt_address6') if virt_address6: server_data['virt_address6'] = virt_address6.split('/')[0] server_data['id'] = doc['_id'] server_data['status'] = True server_data['server_id'] = server_data['id'] server_data['device_name'] = doc['device_name'] server_data['platform'] = doc['platform'] server_data['real_address'] = doc['real_address'] server_data['virt_address'] = doc['virt_address'].split('/')[0] server_data['connected_since'] = doc['connected_since'] if append: svrs = users_data[doc['user_id']]['servers'] svrs.append(server_data) users_data[doc['user_id']]['servers'] = sorted( svrs, key=lambda x: x['name']) net_link_collection = mongo.get_collection('users_net_link') for doc in net_link_collection.find({ 'user_id': { '$in': users_id }, }): users_data[doc['user_id']]['network_links'].append(doc['network']) ip_addrs_iter = server.multi_get_ip_addr(org_id, users_id) for user_id, server_id, addr, addr6 in ip_addrs_iter: server_data = users_servers[user_id].get(server_id) if server_data: if not server_data['virt_address']: server_data['virt_address'] = addr if not server_data['virt_address6']: server_data['virt_address6'] = addr6 if page is not None: resp = { 'page': page, 'page_total': org.page_total, 'server_count': server_count, 'users': users, } elif search is not None: resp = { 'search': search, 'search_more': limit < org.last_search_count, 'search_limit': limit, 'search_count': org.last_search_count, 'search_time': round((time.time() - flask.g.start), 4), 'server_count': server_count, 'users': users, } else: resp = users if settings.app.demo_mode and not search: utils.demo_set_cache(resp, page, search, limit) return utils.jsonify(resp)
def sso_callback_get(): sso_mode = settings.app.sso if sso_mode not in (GOOGLE_AUTH, GOOGLE_DUO_AUTH, GOOGLE_YUBICO_AUTH, SLACK_AUTH, SLACK_DUO_AUTH, SLACK_YUBICO_AUTH, SAML_AUTH, SAML_DUO_AUTH, SAML_YUBICO_AUTH, SAML_OKTA_AUTH, SAML_OKTA_DUO_AUTH, SAML_OKTA_YUBICO_AUTH, SAML_ONELOGIN_AUTH, SAML_ONELOGIN_DUO_AUTH, SAML_ONELOGIN_YUBICO_AUTH): return flask.abort(405) state = flask.request.args.get('state') sig = flask.request.args.get('sig') tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_and_modify(query={ '_id': state, }, remove=True) if not doc: return flask.abort(404) query = flask.request.query_string.split('&sig=')[0] test_sig = base64.urlsafe_b64encode( hmac.new(str(doc['secret']), query, hashlib.sha512).digest()) if not utils.const_compare(sig, test_sig): return flask.abort(401) params = urlparse.parse_qs(query) if doc.get('type') == SAML_AUTH: username = params.get('username')[0] email = params.get('email', [None])[0] groups = set(params.get('groups') or []) org_name = params.get('org', [None])[0] if not username: return flask.abort(406) org_id = settings.app.sso_org if org_name: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id valid, org_id_new, groups2 = sso.plugin_sso_authenticate( sso_type='saml', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), sso_org_names=[org_name], ) if valid: org_id = org_id_new or org_id else: logger.error( 'Saml plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) groups = groups | set(groups2 or []) elif doc.get('type') == SLACK_AUTH: username = params.get('username')[0] email = None user_team = params.get('team')[0] org_names = params.get('orgs', [''])[0] org_names = org_names.split(',') if user_team != settings.app.sso_match[0]: return flask.abort(401) org_id = settings.app.sso_org for org_name in org_names: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id break valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='slack', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), sso_org_names=org_names, ) if valid: org_id = org_id_new or org_id else: logger.error( 'Slack plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) groups = set(groups or []) else: username = params.get('username')[0] email = username valid, google_groups = sso.verify_google(username) if not valid: return flask.abort(401) org_id = settings.app.sso_org valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='google', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: logger.error( 'Google plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) groups = set(groups or []) if settings.app.sso_google_mode == 'groups': groups = groups | set(google_groups) else: for org_name in sorted(google_groups): org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id break if DUO_AUTH in sso_mode: token = utils.generate_secret() tokens_collection = mongo.get_collection('sso_tokens') tokens_collection.insert({ '_id': token, 'type': DUO_AUTH, 'username': username, 'email': email, 'org_id': org_id, 'groups': list(groups) if groups else None, 'timestamp': utils.now(), }) duo_page = static.StaticFile(settings.conf.www_path, 'duo.html', cache=False, gzip=False) sso_duo_mode = settings.app.sso_duo_mode if sso_duo_mode == 'passcode': duo_mode = 'passcode' elif sso_duo_mode == 'phone': duo_mode = 'phone' else: duo_mode = 'push' body_class = duo_mode if settings.app.theme == 'dark': body_class += ' dark' duo_page.data = duo_page.data.replace('<%= body_class %>', body_class) duo_page.data = duo_page.data.replace('<%= token %>', token) duo_page.data = duo_page.data.replace('<%= duo_mode %>', duo_mode) return duo_page.get_response() if YUBICO_AUTH in sso_mode: token = utils.generate_secret() tokens_collection = mongo.get_collection('sso_tokens') tokens_collection.insert({ '_id': token, 'type': YUBICO_AUTH, 'username': username, 'email': email, 'org_id': org_id, 'groups': list(groups) if groups else None, 'timestamp': utils.now(), }) yubico_page = static.StaticFile(settings.conf.www_path, 'yubico.html', cache=False, gzip=False) if settings.app.theme == 'dark': yubico_page.data = yubico_page.data.replace( '<body>', '<body class="dark">') yubico_page.data = yubico_page.data.replace('<%= token %>', token) return yubico_page.get_response() usr = user.find_user_auth(name=username, auth_type=sso_mode) if not usr: org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) else: org = usr.org if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.redirect(utils.get_url_root() + key_link['view_url'])
def sso_authenticate_post(): if settings.app.sso != DUO_AUTH: return flask.abort(405) username = flask.request.json['username'] usernames = [username] email = None if '@' in username: email = username usernames.append(username.split('@')[0]) valid = False for i, username in enumerate(usernames): try: valid, org_id = sso.auth_duo( username, strong=True, ipaddr=flask.request.remote_addr, type='Key', ) break except InvalidUser: if i == len(usernames) - 1: logger.error( 'Invalid duo username', 'sso', username=username, ) if not valid: logger.error( 'Invalid duo username', 'sso', username=username, ) return flask.abort(401) if not org_id: org_id = settings.app.sso_org org = organization.get_by_id(org_id) if not org: logger.error( 'Organization for Duo sso does not exist', 'sso', org_id=org_id, ) return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=DUO_AUTH) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if usr.auth_type != DUO_AUTH: usr.auth_type = DUO_AUTH usr.commit('auth_type') event.Event(type=USERS_UPDATED, resource_id=org.id) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return flask.request.url_root[:-1] + key_link['view_url']
def user_linked_key_page_get(short_code): doc = _find_doc({ 'short_id': short_code, }, one_time=True) if not doc: return flask.abort(404) org = organization.get_by_id(doc['org_id']) user = org.get_user(id=doc['user_id']) if user.disabled: return flask.abort(403) user.audit_event( 'user_profile', 'User temporary profile link viewed', remote_addr=utils.get_remote_addr(), ) if settings.local.sub_active and settings.app.theme == 'dark': view_name = KEY_VIEW_DARK_NAME else: view_name = KEY_VIEW_NAME if user.auth_type == RADIUS_AUTH or \ settings.user.pin_mode == PIN_DISABLED: header_class = 'pin-disabled' else: header_class = '' key_page = static.StaticFile(settings.conf.www_path, view_name, cache=False, gzip=False).data key_page = key_page.replace('<%= header_class %>', header_class) key_page = key_page.replace('<%= user_name %>', '%s - %s' % (org.name, user.name)) key_page = key_page.replace('<%= user_key_tar_url %>', '/key/%s.tar' % (doc['key_id'])) key_page = key_page.replace('<%= user_key_zip_url %>', '/key/%s.zip' % (doc['key_id'])) if org.otp_auth: key_page = key_page.replace('<%= user_otp_key %>', user.otp_secret) key_page = key_page.replace( '<%= user_otp_url %>', 'otpauth://totp/%s@%s?secret=%s' % (user.name, org.name, user.otp_secret)) else: key_page = key_page.replace('<%= user_otp_key %>', '') key_page = key_page.replace('<%= user_otp_url %>', '') if user.pin: key_page = key_page.replace('<%= cur_pin_display %>', 'block') else: key_page = key_page.replace('<%= cur_pin_display %>', 'none') key_page = key_page.replace('<%= key_id %>', doc['key_id']) key_page = key_page.replace('<%= short_id %>', doc['short_id']) conf_links = '' if settings.local.sub_active: conf_links += '<a class="btn btn-success" ' + \ 'title="Download Chromebook Profiles" ' + \ 'href="/key_onc/%s.zip">Download Chromebook Profiles</a><br>\n' % ( doc['key_id']) for server in org.iter_servers(): conf_links += '<a class="btn btn-sm" title="Download Profile" ' + \ 'href="/key/%s/%s.key">Download Profile (%s)</a><br>\n' % ( doc['key_id'], server.id, server.name) key_page = key_page.replace('<%= conf_links %>', conf_links) return key_page
def _auth_plugin(username, password): if settings.local.sub_plan != 'enterprise': return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) valid, org_id, groups = sso.plugin_login_authenticate( user_name=username, password=password, remote_ip=utils.get_remote_addr(), ) if not valid: return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) if not org_id: logger.error( 'Login plugin did not return valid organization name', 'auth', org_name=org_id, user_name=username, ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, type=CERT_CLIENT, auth_type=PLUGIN_AUTH, groups=list(groups) if groups else None) usr.audit_event( 'user_created', 'User created with plugin authentication', remote_addr=utils.get_remote_addr(), ) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return utils.jsonify( { 'error': AUTH_DISABLED, 'error_msg': AUTH_DISABLED_MSG, }, 403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != PLUGIN_AUTH: usr.auth_type = PLUGIN_AUTH usr.set_pin(None) usr.commit(('auth_type', 'pin')) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from plugin authentication', remote_addr=utils.get_remote_addr(), ) return utils.jsonify( { 'redirect': utils.get_url_root() + key_link['view_url'], }, 202)
def _auth_radius(username, password, remote_addr): sso_mode = settings.app.sso valid, org_names, groups = sso.verify_radius(username, password) if not valid: journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_RADIUS_FAILED, reason_long='Radius authentication failed', ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) org_id = settings.app.sso_org if org_names: not_found = False for org_name in org_names: org = organization.get_by_name(org_name, fields=('_id')) if org: not_found = False org_id = org.id break else: not_found = True if not_found: logger.warning( 'Supplied org names do not exists', 'sso', sso_type='radius', user_name=username, org_names=org_names, ) valid, org_id_new, groups2 = sso.plugin_sso_authenticate( sso_type='radius', user_name=username, user_email=None, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Radius plugin authentication failed', ) logger.error( 'Radius plugin authentication not valid', 'sso', username=username, ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) groups = ((groups or set()) | (groups2 or set())) or None if DUO_AUTH in sso_mode: try: duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type='Key', ) valid = duo_auth.authenticate() except InvalidUser: logger.error( 'Duo authentication username not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_DUO_FAILED, reason_long='Duo authentication invalid username', ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) if valid: valid, org_id_new, groups2 = sso.plugin_sso_authenticate( sso_type='duo', user_name=username, user_email=None, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Duo plugin authentication failed', ) logger.error( 'Duo plugin authentication not valid', 'sso', username=username, ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) groups = ((groups or set()) | (groups2 or set())) or None else: logger.error( 'Duo authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_DUO_FAILED, reason_long='Duo authentication failed', ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) groups = ((groups or set()) | (groups2 or set())) or None org = organization.get_by_id(org_id) if not org: logger.error( 'Organization for sso does not exist', 'auth', org_id=org_id, ) return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, type=CERT_CLIENT, auth_type=sso_mode, groups=list(groups) if groups else None) usr.audit_event( 'user_created', 'User created with single sign-on', remote_addr=remote_addr, ) journal.entry( journal.USER_CREATE, usr.journal_data, event_long='User created with single sign-on', remote_address=remote_addr, ) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return utils.jsonify( { 'error': AUTH_DISABLED, 'error_msg': AUTH_DISABLED_MSG, }, 403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.set_pin(None) usr.commit(('auth_type', 'pin')) key_link = org.create_user_key_link(usr.id, one_time=True) journal.entry( journal.SSO_AUTH_SUCCESS, usr.journal_data, key_id_hash=hashlib.md5(key_link['id']).hexdigest(), remote_address=remote_addr, ) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, event_long='User profile viewed from single sign-on', remote_address=remote_addr, ) return utils.jsonify( { 'redirect': utils.get_url_root() + key_link['view_url'], }, 202)
def sso_callback_get(): sso_mode = settings.app.sso if sso_mode not in (GOOGLE_AUTH, GOOGLE_DUO_AUTH, SAML_AUTH, SAML_DUO_AUTH, SAML_OKTA_AUTH, SAML_OKTA_DUO_AUTH, SAML_ONELOGIN_AUTH, SAML_ONELOGIN_DUO_AUTH): return flask.abort(405) state = flask.request.args.get('state') sig = flask.request.args.get('sig') tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_and_modify(query={ '_id': state, }, remove=True) if not doc: return flask.abort(404) query = flask.request.query_string.split('&sig=')[0] test_sig = base64.urlsafe_b64encode( hmac.new(str(doc['secret']), query, hashlib.sha512).digest()) if sig != test_sig: return flask.abort(401) params = urlparse.parse_qs(query) if doc.get('type') == SAML_AUTH: username = params.get('username')[0] email = params.get('email', [None])[0] org_name = params.get('org', [None])[0] if not username: return flask.abort(406) valid, org_name = sso.verify_saml(username, email, org_name) if not valid: return flask.abort(401) org_id = settings.app.sso_org if org_name: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id else: username = params.get('username', [None])[0] email = username valid, org_id = sso.verify_google(username) if not valid: return flask.abort(401) if not org_id: org_id = settings.app.sso_org if DUO_AUTH in sso_mode: valid, _ = sso.auth_duo( username, ipaddr=flask.request.remote_addr, type='Key', ) if not valid: return flask.abort(401) org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return flask.redirect(flask.request.url_root[:-1] + key_link['view_url'])
def key_sync_get(org_id, user_id, server_id, key_hash): utils.rand_sleep() if not settings.local.sub_active: return utils.response('', status_code=480) auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: return flask.abort(401) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: return flask.abort(401) except ValueError: return flask.abort(401) org = organization.get_by_id(org_id) if not org: return flask.abort(404) user = org.get_user(id=user_id) if not user: return flask.abort(404) elif not user.sync_secret: return flask.abort(404) auth_string = '&'.join([ auth_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path ] + ([flask.request.data] if flask.request.data else [])) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: return flask.abort(401) auth_test_signature = base64.b64encode( hmac.new(user.sync_secret.encode(), auth_string, hashlib.sha256).digest()) if auth_signature != auth_test_signature: return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert( { 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }, w=0) except pymongo.errors.DuplicateKeyError: return flask.abort(401) key_conf = user.sync_conf(server_id, key_hash) if key_conf: return utils.response(key_conf['conf']) return utils.response('')
def generate_ovpn_conf(self): logger.debug( 'Generating server ovpn conf', 'server', server_id=self.server.id, ) if not self.server.primary_organization or \ not self.server.primary_user: self.server.create_primary_user() if self.server.primary_organization not in self.server.organizations: self.server.remove_primary_user() self.server.create_primary_user() primary_org = organization.get_by_id(self.server.primary_organization) if not primary_org: self.server.create_primary_user() primary_org = organization.get_by_id( id=self.server.primary_organization) self.primary_user = primary_org.get_user(self.server.primary_user) if not self.primary_user: self.server.create_primary_user() primary_org = organization.get_by_id( id=self.server.primary_organization) self.primary_user = primary_org.get_user(self.server.primary_user) push = '' if self.server.mode == LOCAL_TRAFFIC: for network in self.server.local_networks: push += 'push "route %s %s"\n' % utils.parse_network(network) elif self.server.mode == VPN_TRAFFIC: pass for link_svr in self.server.iter_links(fields=('_id', 'network', 'local_networks', 'network_start', 'network_end')): if self.server.id < link_svr.id: gateway = utils.get_network_gateway(self.server.network) push += 'route %s %s %s\n' % ( utils.parse_network(link_svr.network) + (gateway, )) for local_network in link_svr.local_networks: push += 'route %s %s %s\n' % ( utils.parse_network(local_network) + (gateway, )) if self.server.network_mode == BRIDGE: host_int_data = self.host_interface_data host_address = host_int_data['address'] host_netmask = host_int_data['netmask'] server_line = 'server-bridge %s %s %s %s' % ( host_address, host_netmask, self.server.network_start, self.server.network_end, ) else: server_line = 'server %s %s' % utils.parse_network( self.server.network) server_conf = OVPN_INLINE_SERVER_CONF % ( self.server.port, self.server.protocol, self.interface, server_line, self.management_socket_path, self.server.max_clients, self.server.ping_interval, self.server.ping_timeout + 20, self.server.ping_interval, self.server.ping_timeout, CIPHERS[self.server.cipher], 4 if self.server.debug else 1, 8 if self.server.debug else 3, ) if self.server.bind_address: server_conf += 'local %s\n' % self.server.bind_address if self.server.inter_client: server_conf += 'client-to-client\n' if self.server.multi_device: server_conf += 'duplicate-cn\n' # Pritunl v0.10.x did not include comp-lzo in client conf # if lzo_compression is adaptive dont include comp-lzo in server conf if self.server.lzo_compression == ADAPTIVE: pass elif self.server.lzo_compression: server_conf += 'comp-lzo yes\npush "comp-lzo yes"\n' else: server_conf += 'comp-lzo no\npush "comp-lzo no"\n' server_conf += JUMBO_FRAMES[self.server.jumbo_frames] if push: server_conf += push if self.server.debug: self.server.output.push_message('Server conf:') for conf_line in server_conf.split('\n'): if conf_line: self.server.output.push_message(' ' + conf_line) server_conf += '<ca>\n%s\n</ca>\n' % self.server.ca_certificate if self.server.tls_auth: server_conf += 'key-direction 0\n<tls-auth>\n%s\n</tls-auth>\n' % ( self.server.tls_auth_key) server_conf += '<cert>\n%s\n</cert>\n' % utils.get_cert_block( self.primary_user.certificate) server_conf += '<key>\n%s\n</key>\n' % self.primary_user.private_key server_conf += '<dh>\n%s\n</dh>\n' % self.server.dh_params with open(self.ovpn_conf_path, 'w') as ovpn_conf: os.chmod(self.ovpn_conf_path, 0600) ovpn_conf.write(server_conf)
def generate_ovpn_conf(self): if not self.server.primary_organization or \ not self.server.primary_user: self.server.create_primary_user() if self.server.primary_organization not in self.server.organizations: self.server.remove_primary_user() self.server.create_primary_user() primary_org = organization.get_by_id(self.server.primary_organization) if not primary_org: self.server.create_primary_user() primary_org = organization.get_by_id( id=self.server.primary_organization) self.primary_user = primary_org.get_user(self.server.primary_user) if not self.primary_user: self.server.create_primary_user() primary_org = organization.get_by_id( id=self.server.primary_organization) self.primary_user = primary_org.get_user(self.server.primary_user) gateway = utils.get_network_gateway(self.server.network) gateway6 = utils.get_network_gateway(self.server.network6) push = '' routes = [] for route in self.server.get_routes(include_default=False): routes.append(route['network']) if route['virtual_network']: continue metric = route.get('metric') if metric: metric_def = ' default %s' % metric metric = ' %s' % metric else: metric_def = '' metric = '' network = route['network'] if route['net_gateway']: if ':' in network: push += 'push "route-ipv6 %s net_gateway%s"\n' % (network, metric) else: push += 'push "route %s %s net_gateway%s"\n' % ( utils.parse_network(network) + (metric, )) elif not route.get('network_link'): if ':' in network: push += 'push "route-ipv6 %s%s"\n' % (network, metric_def) else: push += 'push "route %s %s%s"\n' % ( utils.parse_network(network) + (metric_def, )) else: if ':' in network: push += 'route-ipv6 %s %s%s\n' % (network, gateway6, metric) else: push += 'route %s %s %s%s\n' % ( utils.parse_network(network) + (gateway, metric)) for link_svr in self.server.iter_links( fields=('_id', 'network', 'local_networks', 'network_start', 'network_end', 'organizations', 'routes', 'links', 'ipv6', 'replica_count', 'network_mode')): if self.server.id < link_svr.id: for route in link_svr.get_routes(include_default=False): network = route['network'] metric = route.get('metric') if metric: metric = ' %s' % metric else: metric = '' if route['net_gateway']: continue if ':' in network: push += 'route-ipv6 %s %s%s\n' % (network, gateway6, metric) else: push += 'route %s %s %s%s\n' % ( utils.parse_network(network) + (gateway, metric)) if self.vxlan: push += 'push "route %s %s"\n' % utils.parse_network( self.vxlan.vxlan_net) if self.server.ipv6: push += 'push "route-ipv6 %s"\n' % self.vxlan.vxlan_net6 if self.server.network_mode == BRIDGE: host_int_data = self.host_interface_data host_address = host_int_data['address'] host_netmask = host_int_data['netmask'] server_line = 'server-bridge %s %s %s %s' % ( host_address, host_netmask, self.server.network_start, self.server.network_end, ) else: server_line = 'server %s %s' % utils.parse_network( self.server.network) if self.server.ipv6: server_line += '\nserver-ipv6 ' + self.server.network6 if self.server.protocol == 'tcp': if (self.server.ipv6 or settings.vpn.ipv6) and \ not self.server.bind_address: protocol = 'tcp6-server' else: protocol = 'tcp-server' elif self.server.protocol == 'udp': if (self.server.ipv6 or settings.vpn.ipv6) and \ not self.server.bind_address: protocol = 'udp6' else: protocol = 'udp' else: raise ValueError('Unknown protocol') if utils.check_openvpn_ver(): server_ciphers = SERVER_CIPHERS server_conf_template = OVPN_INLINE_SERVER_CONF else: server_ciphers = SERVER_CIPHERS_OLD server_conf_template = OVPN_INLINE_SERVER_CONF_OLD server_conf = server_conf_template % ( self.server.port, protocol, self.interface, server_line, self.management_socket_path, self.server.max_clients, self.server.ping_interval, self.server.ping_timeout + 20, self.server.ping_interval, self.server.ping_timeout, server_ciphers[self.server.cipher], HASHES[self.server.hash], 4 if self.server.debug else 1, 8 if self.server.debug else 3, ) if self.server.bind_address: server_conf += 'local %s\n' % self.server.bind_address if self.server.inter_client: server_conf += 'client-to-client\n' if self.server.multi_device: server_conf += 'duplicate-cn\n' if self.server.protocol == 'udp': server_conf += 'replay-window 128\n' if self.server.mss_fix: server_conf += 'mssfix %s\n' % self.server.mss_fix # Pritunl v0.10.x did not include comp-lzo in client conf # if lzo_compression is adaptive dont include comp-lzo in server conf if self.server.lzo_compression == ADAPTIVE: pass elif self.server.lzo_compression: server_conf += 'comp-lzo yes\npush "comp-lzo yes"\n' else: server_conf += 'comp-lzo no\npush "comp-lzo no"\n' server_conf += JUMBO_FRAMES[self.server.jumbo_frames] if push: server_conf += push if self.server.debug: self.server.output.push_message('Server conf:') for conf_line in server_conf.split('\n'): if conf_line: self.server.output.push_message(' ' + conf_line) if settings.local.sub_plan and \ 'enterprise' in settings.local.sub_plan: returns = plugins.caller( 'server_config', host_id=settings.local.host_id, host_name=settings.local.host.name, server_id=self.server.id, server_name=self.server.name, port=self.server.port, protocol=self.server.protocol, ipv6=self.server.ipv6, ipv6_firewall=self.server.ipv6_firewall, network=self.server.network, network6=self.server.network6, network_mode=self.server.network_mode, network_start=self.server.network_start, network_stop=self.server.network_end, restrict_routes=self.server.restrict_routes, bind_address=self.server.bind_address, onc_hostname=None, dh_param_bits=self.server.dh_param_bits, multi_device=self.server.multi_device, dns_servers=self.server.dns_servers, search_domain=self.server.search_domain, otp_auth=self.server.otp_auth, cipher=self.server.cipher, hash=self.server.hash, inter_client=self.server.inter_client, ping_interval=self.server.ping_interval, ping_timeout=self.server.ping_timeout, link_ping_interval=self.server.link_ping_interval, link_ping_timeout=self.server.link_ping_timeout, allowed_devices=self.server.allowed_devices, max_clients=self.server.max_clients, replica_count=self.server.replica_count, dns_mapping=self.server.dns_mapping, debug=self.server.debug, routes=routes, interface=self.interface, bridge_interface=self.bridge_interface, vxlan=self.vxlan, ) if returns: for return_val in returns: if not return_val: continue server_conf += return_val.strip() + '\n' server_conf += '<ca>\n%s\n</ca>\n' % self.server.ca_certificate if self.server.tls_auth: server_conf += \ 'key-direction 0\n<tls-auth>\n%s\n</tls-auth>\n' % ( self.server.tls_auth_key) server_conf += '<cert>\n%s\n</cert>\n' % utils.get_cert_block( self.primary_user.certificate) server_conf += '<key>\n%s\n</key>\n' % self.primary_user.private_key server_conf += '<dh>\n%s\n</dh>\n' % self.server.dh_params with open(self.ovpn_conf_path, 'w') as ovpn_conf: os.chmod(self.ovpn_conf_path, 0600) ovpn_conf.write(server_conf)
def sso_callback_get(): sso_mode = settings.app.sso if sso_mode not in (GOOGLE_AUTH, GOOGLE_DUO_AUTH, SLACK_AUTH, SLACK_DUO_AUTH, SAML_AUTH, SAML_DUO_AUTH, SAML_OKTA_AUTH, SAML_OKTA_DUO_AUTH, SAML_ONELOGIN_AUTH, SAML_ONELOGIN_DUO_AUTH): return flask.abort(405) state = flask.request.args.get('state') sig = flask.request.args.get('sig') tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_and_modify(query={ '_id': state, }, remove=True) if not doc: return flask.abort(404) query = flask.request.query_string.split('&sig=')[0] test_sig = base64.urlsafe_b64encode( hmac.new(str(doc['secret']), query, hashlib.sha512).digest()) if sig != test_sig: return flask.abort(401) params = urlparse.parse_qs(query) if doc.get('type') == SAML_AUTH: username = params.get('username')[0] email = params.get('email', [None])[0] org_name = params.get('org', [None])[0] if not username: return flask.abort(406) valid, org_name = sso.verify_saml(username, email, org_name) if not valid: return flask.abort(401) org_id = settings.app.sso_org if org_name: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='saml', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), sso_org_names=[org_name], ) if valid: org_id = org_id_new or org_id else: logger.error( 'Saml plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) elif doc.get('type') == SLACK_AUTH: username = params.get('username')[0] email = None user_team = params.get('team')[0] org_names = params.get('orgs', [''])[0] org_names = org_names.split(',') valid, org_name = sso.verify_slack(username, user_team, org_names) if not valid: return flask.abort(401) if org_name: org_names = [org_name] org_id = settings.app.sso_org for org_name in org_names: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id break valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='slack', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), sso_org_names=org_names, ) if valid: org_id = org_id_new or org_id else: logger.error( 'Slack plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) else: username = params.get('username')[0] email = username valid, org_name = sso.verify_google(username) if not valid: return flask.abort(401) org_id = settings.app.sso_org if org_name: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='google', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: logger.error( 'Google plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) if DUO_AUTH in sso_mode: valid, _ = sso.auth_duo( username, ipaddr=utils.get_remote_addr(), type='Key', ) if valid: valid, org_id_new, groups2 = sso.plugin_sso_authenticate( sso_type='duo', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: logger.error( 'Duo plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) groups = ((groups or set()) | (groups2 or set())) or None else: logger.error( 'Duo authentication not valid', 'sso', username=username, ) return flask.abort(401) org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.redirect(utils.get_url_root() + key_link['view_url'])