def get(self, request, app_id, login=""): try: app = Application.objects( id=ObjectId(app_id)).no_dereference().only( 'name', 'auth_backend', 'sso_profile').first() if not app: raise Application.DoesNotExist() except (Application.DoesNotExist, InvalidId) as e: logger.error( "SSOProfiles::GET: Application with id '{}' not found".format( app_id)) return JsonResponse({ 'status': False, 'error': "Application with id '{}' not found.".format(app_id) }) insecure = str(request.GET.get('insecure', False)).lower() == "true" try: if login: sso_profiles = app.get_sso_profiles(login, insecure=insecure) else: sso_profiles = app.get_all_sso_profiles(insecure=insecure) return JsonResponse({'status': True, 'sso_profiles': sso_profiles}) except Exception as e: logger.critical(e, exc_info=1) return JsonResponse({'status': False, 'error': str(e)})
def get_all(request, fields=None): """ return a list of dumps of all applications. Fields can be given to filter out the result """ try: fields = fields.split("|") except Exception: pass try: to_return = [] for app in Application.objects(): to_return.append(app.dump(fields)) return JsonResponse(to_return, content_type='application/json', safe=False) except Exception as error: logger.exception(error) return JsonResponse({ 'status': False, 'error': str(error) }, content_type='application/json')
def get_by_regex(request, regex, fields=None): """ return one or several dump(s) according to the regex given. Fields can be given to filter out the result """ try: fields = fields.split("|") except Exception: pass try: app_list = Application.get_apps_by_regex(regex) to_return = [] for app in app_list: to_return.append(app.dump(fields)) return JsonResponse(to_return, content_type='application/json', safe=False) except Exception as error: logger.exception(error) return JsonResponse({ 'status': False, 'error': str(error) }, content_type='application/json')
def get(self, request, object_id): obj_inst = self.obj.objects.with_id(ObjectId(object_id)) if not obj_inst: return HttpResponseForbidden("Injection detected.") if Application.objects(log_custom=obj_inst): self.template_name = 'generic_delete_forbidden.html' return super(DeleteModLog, self).get(request, object_id)
def report_pf(request): apps = [] for app in Application.objects(): if app.log_custom.repository_type == 'data': apps.append(app) apps.sort(key=lambda x: x.name) cluster = Cluster.objects.get() node = cluster.get_current_node() packet_filter = [] for node in cluster.members: try: if node.system_settings.pf_settings.repository_type == 'data': packet_filter.append({ 'id': node.id, 'name': node.name, 'repo': node.system_settings.pf_settings.repository.type_uri }) except: pass if not request.is_ajax(): return render_to_response('report_pf.html', { 'apps': apps, 'packet_filter': packet_filter }, context_instance=RequestContext(request))
def report_security(request): apps = [] for app in Application.objects(): if app.log_custom.repository_type == 'data': apps.append(app) apps.sort(key=lambda x: x.name) if not request.is_ajax(): return render_to_response('report_security.html', {'apps': apps}, context_instance=RequestContext(request))
def generate(request, is_reload=False): """ generate a new application with a given description """ try: is_reload = (is_reload == "reload") except Exception: pass try: Application.generate(json.loads(request.body), is_reload) return JsonResponse({'status': True}, content_type='application/json') except Exception as error: logger.exception(error) return JsonResponse({ 'status': False, 'error': str(error) }, content_type='application/json')
def is_deletable(self): """ Method used to know if template object is used by an Application. If object is used, user can't delete PortalTemplate :return: True if object is not used, False otherwise """ from gui.models.application_settings import Application used = Application.objects(template=self) if used: return False return True
def is_deletable(self): """ Method used to know if Modlog object is used by an Application. If object is used, user can't delete Modlog :return: True if object is not used, False otherwise """ from gui.models.application_settings import Application used = Application.objects(log_custom=self) if used: return False return True
def delete(self, request, app_id, login=""): if not login: return JsonResponse({'status': False, 'error': "Login missing."}) try: app = Application.objects( id=ObjectId(app_id)).no_dereference().only( 'name', 'auth_backend', 'sso_profile').first() if not app: raise Application.DoesNotExist() except (Application.DoesNotExist, InvalidId) as e: logger.error( "SSOProfiles::GET: Application with id '{}' not found".format( app_id)) return JsonResponse({ 'status': False, 'error': "Application with id '{}' not found.".format(app_id) }) try: # Check is an sso_profile does exists if SSOProfile.objects.filter(app_id=str(app.id), repo_id=str(app.getAuthBackend().id), login=login).count() == 0: return JsonResponse({ 'status': False, 'error': "No SSOProfile found with application '{}', repository '{}' " "and login '{}'".format(app.name, app.getAuthBackend().repo_name, login) }) app.delete_sso_profile(login) return JsonResponse({'status': True}) except Exception as e: logger.exception(e) return JsonResponse({'status': False, 'error': str(e)}, status=500)
def post(self, request, object_id): confirm = request.POST.get('confirm') if confirm == 'yes': obj_inst = self.obj.objects.with_id(ObjectId(object_id)) if not obj_inst: return HttpResponseForbidden("Injection detected.") # Check if ModLog Object is not used by an Application if not Application.objects(log_custom=obj_inst): obj_inst.delete() else: pass return HttpResponseRedirect(self.redirect_url)
def post(self, request, object_id): confirm = request.POST.get('confirm') if confirm == 'yes': obj_inst = self.obj.objects.with_id(ObjectId(object_id)) if not obj_inst: return HttpResponseForbidden("Injection detected.") app_list = Application.objects(modsec_policy=obj_inst) if not len(app_list): obj_inst.delete() return HttpResponseRedirect(self.redirect_url)
def policy_list(request): """ Page dedicated to show mod_security profiles list """ try: modsec_list = ModSec.objects() policy_list = [{ "modsec": item, "is_delible": False } if len(Application.objects( modsec_policy=item).values_list("name")) else { "modsec": item, "is_delible": True } for item in modsec_list] except: policy_list = None return render_to_response('modsec.html', {'policy_list': policy_list}, context_instance=RequestContext(request))
def worker_list(request): """ Page dedicated to show worker list """ try: workers = Worker.objects() except Worker.DoesNotExist: workers = None worker_list = list() for worker in workers: worker.applist = list() for app in Application.objects(): if app.worker == worker: worker.applist.append(app) worker_list.append(worker) workers = worker_list return render_to_response('worker.html', {'workers': workers}, context_instance=RequestContext(request))
def perform_action(self): # Retrieve all the hkeys in redis matching "backend_(app_id) = (backend_id)" # with portal_cookie backends_apps = self.redis_portal_session.get_auth_backends() backends = list() apps = list() for key, item in backends_apps.items(): # Extract the id of the application in "backend_(id)" app = key[8:] if app not in apps: apps.append(app) # The item is the backend if item not in backends: backends.append(item) logger.debug( "User successfully authenticated on following apps : '{}'".format( apps)) logger.debug( "User successfully authenticated on following backends : '{}'". format(backends)) # Retrieve all the apps which need auth AND which the backend or backend_fallback is in common with the backend the user is logged on # And retrieve all the apps that does not need authentication Query = (Q(need_auth=True) & (Q(auth_backend__in=backends) | Q(auth_backend_fallbacks__in=backends))) | Q(need_auth=False) auth_apps = Application.objects(Query).only('name', 'public_name', 'public_dir', 'id', 'type', 'listeners', 'need_auth') final_apps = list() for app in auth_apps: final_apps.append({ 'name': app.name, 'url': str(app.get_redirect_uri()), 'status': app.need_auth and str(app.id) in apps }) return final_apps
def get_by_name(request, app_name, fields=None): """ return a dump of an application according to the name given. Fields can be given to filter out the result """ try: fields = fields.split("|") except Exception: pass try: app = Application.get_app_by_name(app_name) return JsonResponse(app.dump(fields), content_type='application/json') except Exception as error: logger.exception(error) return JsonResponse({ 'status': False, 'error': str(error) }, content_type='application/json')
def application_list(request): """ Page dedicated to show application list """ l_list = dict() new_list = {} #FIXME: We should display ListenAddress instead of Listeners """ Build the list of listeners Reminder: A listener is associated to an APP and refers to a ListenAddress There may have many listeners for a given ListenAddress Here, the listener list is used to display the PLAY / PAUSE / RELOAD buttons We build a dictionary indexed on IP:PORT => We need to be carefull and display listeners that needs to be reloaded """ listeners = ListenAddress.objects() for l in listeners: lst = str(l.address) + ': ' + str(l.port) node_name = l.get_related_node().name if node_name not in new_list.keys(): new_list[node_name] = [] """ Need to be reloaded => add it ! """ if l.is_up2date is False: l_list[lst] = l new_list[node_name].append(l) else: if not l_list.get(lst): new_list[node_name].append(l) l_list[lst] = l return render_to_response('application.html', { 'listeners': l_list, 'apps': Application.objects(), 'new_listeners': new_list }, context_instance=RequestContext(request))
def import_application(conn, headerid, modlog_dict, modssl_dict): """ import all applications from sqlite3 :param conn: sqlite3 connector :param headerid: dict to retrieve sqlite3 id of header generated in previous method :param modlog_dict: dict to retrieve sqlite3 id of modlog generated in previous method :param modssl_dict: dict to retrieve sqlite3 id of ssl profile generated in previous method """ appid_application = dict() app_log = dict() appid_logid = dict() appid_headerid = dict() appid_intfid = dict() listenertolistenaddress = dict() appid_interfaceid = dict() cluster = Cluster.objects.get() node = cluster.get_current_node() # select the first interface which is em0 # FIXME: ask user on which interface to bind all the listeners # FIXME : Obsolet method em = node.get_interfaces()[0].id # generate list for every table in sqlite database intf = conn.cursor() intf.execute("SELECT * FROM intf") intf_sql = intf.fetchall() app = conn.cursor() app.execute("SELECT * FROM app") application_sql = app.fetchall() app_intf = conn.cursor() app_intf.execute("SELECT * FROM app_intf") app_intf_sql = app_intf.fetchall() log = conn.cursor() log.execute("SELECT * FROM log") log_sql = log.fetchall() header = conn.cursor() header.execute("SELECT * FROM header") header_sql = header.fetchall() plugincontent = conn.cursor() plugincontent.execute("SELECT * FROM plugincontent") plugincontent_sql = plugincontent.fetchall() pluginheader = conn.cursor() pluginheader.execute("SELECT * FROM pluginheader") pluginheader_sql = pluginheader.fetchall() logger.info("Importing network interfaces") # Retrieve index in database of each name fields of "app_intf" table col_name_list_app_intf = [tuple[0] for tuple in app_intf.description] app_intf_id = col_name_list_app_intf.index("id") app_intf_app_id = col_name_list_app_intf.index("app_id") app_intf_intf_id = col_name_list_app_intf.index("intf_id") # Retrieve index in database of each name fields of "intf" table col_name_list_intf = [tuple[0] for tuple in intf.description] intf_id = col_name_list_intf.index("id") intf_name = col_name_list_intf.index("name") intf_ip = col_name_list_intf.index("ip") intf_port = col_name_list_intf.index("port") intf_log_id = col_name_list_intf.index("log_id") # Retrieve index in database of each name fields of "app" table col_name_list_app = [tuple[0] for tuple in app.description] app_id = col_name_list_app.index("id") app_friendly_name = col_name_list_app.index("friendly_name") app_name = col_name_list_app.index("name") app_url = col_name_list_app.index("url") app_log_id = col_name_list_app.index("log_id") app_logon_url = col_name_list_app.index("logon_url") app_auth_id = col_name_list_app.index("auth_id") app_auth_url = col_name_list_app.index("auth_url") app_Balancer_Name = col_name_list_app.index("Balancer_Name") app_Balancer_Node = col_name_list_app.index("Balancer_Node") app_enable_ssl = col_name_list_app.index("enable_ssl") app_ssl_configuration_id = col_name_list_app.index("ssl_configuration_id") # Retrieve index in database of each name fields of "log" table col_name_list_log = [tuple[0] for tuple in log.description] logs_id = col_name_list_log.index("id") logs_name = col_name_list_log.index("name") logs_level = col_name_list_log.index("level") logs_format = col_name_list_log.index("format") # Retrieve index in database of each name fields of "header" table col_name_list_header = [tuple[0] for tuple in header.description] header_id = col_name_list_header.index("id") header_name = col_name_list_header.index("name") header_type = col_name_list_header.index("type") header_value = col_name_list_header.index("value") header_app_id = col_name_list_header.index("app_id") # Retrieve index in database of each name fields of "pluginheader" table col_name_list_plugin_header = [tuple[0] for tuple in pluginheader.description] pluginheader_id = col_name_list_plugin_header.index("id") pluginheader_app_id = col_name_list_plugin_header.index("app_id") pluginheader_pattern = col_name_list_plugin_header.index("pattern") pluginheader_type = col_name_list_plugin_header.index("type") pluginheader_options = col_name_list_plugin_header.index("options") pluginheader_options1= col_name_list_plugin_header.index("options1") # Retrieve index in database of each name fields of "plugincontent" table col_name_list_plugin_content = [tuple[0] for tuple in plugincontent.description] plugincontent_id = col_name_list_plugin_content.index("id") plugincontent_app_id = col_name_list_plugin_content.index("app_id") plugincontent_pattern = col_name_list_plugin_content.index("pattern") plugincontent_type = col_name_list_plugin_content.index("type") plugincontent_options = col_name_list_plugin_content.index("options") plugincontent_options1 = col_name_list_plugin_content.index("options1") for i in app_intf_sql: appid_interfaceid[i[app_intf_app_id]] = i[app_intf_intf_id] # generate every listener on the current node for i in intf_sql: logger.info("Listener " + str(i[intf_name])) node = cluster.get_current_node() listener = Listener() listener.alias = str(i[intf_name]) listener.ip = str(i[intf_ip]) listener.version = "4" listener.prefixlen = "255.255.255.0" listener.is_carp = False listener.is_physical = False listener.is_gui = False listener.is_ghost = False listener.save() # dictionary to link listener to listen address listenertolistenaddress[i[intf_id]] = listener logger.info("Creating listener " + str(listener.alias)) selected_intf = Interface.objects.with_id(em) selected_intf.inet_addresses.append(listener) # append the listen address in the interface selected_intf.save() listener.deploy_inet() node.interfaces[intf_id] = selected_intf # add the interface modified into the node node.save() appid_listenaddress = dict() logger.info("End of network interfaces import process") logger.info("Importing applications") for app in application_sql: application = Application() outgoing_headers = [] content_rules = [] headers_in = [] public_dir = "" # Retrieve fqdn and public_dir from app[app_friendly_name] tmp = app[app_friendly_name].split("/") fqdn = tmp[0] public_dir = app[app_friendly_name].replace(fqdn, "") if not public_dir.endswith('/'): public_dir = public_dir + '/' # if there's an authentication portal (app[app_logon_url]) then auth = True if app[app_logon_url] is None: need_auth = False else: need_auth = True # retrieve type of application if app[app_url].split(":")[0] == "balancer": # if the url start with "balancer://" , app_type=balanced app_type = "balanced" # [app_Balancer_Name] = balancer uri application.proxy_balancer = ProxyBalancer.objects.get(name=app[app_Balancer_Name]) else: app_type = app[app_url].split(":")[0] # otherwise; filetype is before ":" (http,https,ftp...) # retrieve log profile linked to this app (we generated the profile earlier, now we find it back) for j in log_sql: if app[app_log_id] == j[logs_id]: # if app[app_log_id] =logs_id of the application match => link app_log[app[app_friendly_name]] = j[logs_level] # link appname -> log_level found appid_logid[app[app_id]] = j[logs_id] # generate a listen_address for this app based on the interface generated earlier for j in intf_sql: if appid_interfaceid[app[app_id]] == j[intf_id]: # find link between interface and app_id interface_id = j[intf_id] intf.execute("SELECT * FROM intf WHERE id =?", (interface_id,)) # select interface in db with link interface = intf.fetchone() appid_intfid[app[app_id]] = interface[0] # appid_interfaceid appid:interfaceid listen_address = ListenAddress() # apprend address of listener to current listen address listen_address.address = listenertolistenaddress[j[intf_id]] listen_address.port = str(j[intf_port]) listen_address.redirect = False listen_address.redirect_port = "" # if this app uses a specific SSL profile, then load it if app[app_enable_ssl] == 1: listen_address.ssl_profile = modssl_dict[app[app_ssl_configuration_id]] listen_address.save() appid_listenaddress[app[app_id]] = listen_address # Retrieve all request headers except SSL headers (not managed in vulture3) for j in header_sql: if app[app_id] == j[header_app_id]: if j[header_name] != "SSL_CLIENT_S_DN_CN" and \ j[header_name] != "SSL_CLIENT_S_DN_O" and j[header_name] != "SSL_CLIENT_S_DN_OU": appid_headerid[str(app[app_id])] = j[header_id] headers_in.append(headerid[j[header_id]]) # Retrieve headers in "pluginheader" table for j in pluginheader_sql: if j[pluginheader_app_id] == app[app_id]: headerin = HeaderIn() headerin.name = j[pluginheader_pattern] headerin.action = "edit" headerin.value = j[pluginheader_options] headerin.replacement = j[pluginheader_options1] headerin.condition = "always" headerin.save() headers_in.append(headerin) """ generate each type of header out and contentrule. Stored in the same table "plugincontent" with non consistent fields.""" for i in plugincontent_sql: if i[plugincontent_type] == "Rewrite Content" and i[plugincontent_app_id] == app[app_id]: content_rule = ContentRule() content_rule.path_type = "files" content_rule.path = "" content_rule.types = "" content_rule.flags = "" content_rule.deflate = True content_rule.inflate = False content_rule.pattern = i[plugincontent_pattern] content_rule.replacement = i[plugincontent_options] content_rule.save() content_rules.append(content_rule) if i[plugincontent_type] == "Rewrite Link" and i[plugincontent_app_id] == app[app_id]: headerout = HeaderOut() headerout.name = "Link" headerout.value = i[plugincontent_options] headerout.replacement = i[plugincontent_pattern] headerout.action = "edit" headerout.condition = "always" headerout.save() outgoing_headers.append(headerout) if i[plugincontent_type] == "Location" and i[plugincontent_app_id] == app[app_id]: headerout = HeaderOut() headerout.name = i[plugincontent_pattern] headerout.value = i[plugincontent_options1] headerout.replacement = i[plugincontent_options] headerout.action = "edit" headerout.condition = "always" headerout.save() outgoing_headers.append(headerout) # retrieve the Vulture Internal Database repository for authentication. repo_list = BaseAbstractRepository.get_auth_repositories() for repo in repo_list: if repo.is_internal and isinstance(repo.get_backend(), MongoEngineBackend): auth_backend = repo # select the default worker worker = Worker.objects.first() # auth_backend = "" # populate each field of app generated before application.name = app[app_friendly_name] application.type = app_type application.public_name = fqdn application.private_uri = app[app_url] application.public_dir = public_dir application.proxy_add_header = False application.access_mode = [] # retrieve log profile generated that have a link with this app for j in log_sql: application.log_custom = modlog_dict[appid_logid[app[app_id]]] tmp = application.log_custom.format tmp = tmp.replace("\\", "") # remove escaping chars from mongo application.log_custom.format = tmp application.log_level = app_log[app[app_friendly_name]] application.headers_in = headers_in application.headers_out = outgoing_headers application.content_rules = content_rules application.listeners.append(appid_listenaddress[app[app_id]]) application.force_tls = False application.worker = worker application.methods = "HEAD,GET,POST" application.enable_h2 = False application.enable_rpc = False application.need_auth = need_auth if need_auth: application.auth_type = "basic" application.auth_backend = str(auth_backend) application.auth_backend_fallback = str(auth_backend) application.auth_portal = str(application.private_uri + app[app_logon_url]) application.auth_timeout = "900" application.auth_timeout_restart = True application.sso_enabled = False if application.sso_enabled: application.sso_forward = "form" application.sso_forward_basic_mode = "autologon" application.sso_forward_only_login = False application.sso_forward_basic_url = "http=//your_internal_app/action.do?what=login" application.sso_forward_follow_redirect = False application.sso_forward_return_post = False application.sso_forward_content_type = "default" application.sso_url = "http://your_internal_app/action.do?what=login" application.sso_vulture_agent = False application.sso_capture_content_enabled = False application.sso_capture_content = "" application.sso_replace_content_enabled = False application.sso_replace_content = "By previously captured '$1'/" application.sso_after_post_request_enabled = False application.sso_after_post_request = "http://My_Responsive_App.com/Default.aspx" application.sso_after_post_replace_content_enabled = False application.sso_after_post_replace_content = "" logger.info("Saving application: " + str(application)) application.save() appid_application[app[app_id]] = application.id logger.info("End of applications import process") return appid_application
def edit(request, object_id=None): """ View dedicated to application management :param object_id: MongoDB object_id of application :param request: Django request object """ # Retrieving application configuration application = Application.objects.with_id(ObjectId(object_id)) specific_rules_set = [] incoming_headers = [] outgoing_headers = [] content_rules = [] listeners = [] listeners_ips = [] svms = [] activated_svms = [] # Application doesn"'t exist ==> We want to create a new one # Fix some default values if not application and request.method != 'POST': application = Application(name="My App", type="http", public_name="www.example.com", public_alias="www.ex_1.fr", public_dir="/", private_uri="https://192.168.1.1/owa/") # Fix default security rules incoming_headers.append( HeaderOut(True, 'unset', '^X-Forwarded-', '', '', 'always', '')) # outgoing_headers.append(HeaderOut ('set', 'Content-Security-Policy', 'default-src \'self\'', '', 'always', '')) outgoing_headers.append( HeaderOut(True, 'set', 'X-Frame-Options', 'SAMEORIGIN', '', 'always', '')) outgoing_headers.append( HeaderOut(True, 'set', 'X-Content-Type-Options', 'nosniff', '', 'always', '')) outgoing_headers.append( HeaderOut(True, 'set', 'X-XSS-Protection', '1; mode=block', '', 'always', '')) application.headers_in = incoming_headers application.headers_out = outgoing_headers # Check if request are valid - and populate arrays with form values if request.method == 'POST': dataPosted = request.POST dataPostedRaw = str(request.body).split("&") has_listener = False dataPosted_length = len(dataPostedRaw) if application and not "enabled=on" in dataPostedRaw: application.delete_listeners() for data in dataPostedRaw: if data.startswith("address_"): # Listener management for cpt in range(dataPosted_length): if data.startswith("address_" + str(cpt) + "="): inet = None port = None redirect_port = None try: listener_id = dataPosted['address_' + str(cpt)] inet = Listener.objects.with_id( ObjectId(listener_id)) except Exception as e: pass try: port = dataPosted['port_' + str(cpt)] except Exception as e: pass try: redirect_port = dataPosted['redirect_port_' + str(cpt)] except Exception as e: pass try: ssl_profile = ModSSL.objects.with_id( ObjectId(dataPosted['ssl_profile_' + str(cpt)])) except Exception as e: ssl_profile = None pass if (inet and port): address = ListenAddress( address=inet, port=port, ssl_profile=ssl_profile, redirect_port=redirect_port) """ Add '1' because we don't want to save yet the listener """ address.related_node = address.get_related_node(1) listeners.append(address) listeners_ips.append( (address.related_node, inet.ip, port)) has_listener = True dataPosted_length -= 4 if data.startswith("SpecificRS_url_"): for cpt in range(dataPosted_length): if data.startswith("SpecificRS_url_" + str(cpt) + "="): # Force default values to prevent injection or any problem specific_rs_url = dataPosted.get( 'SpecificRS_url_' + str(cpt), '/login/') try: specific_rs_rs = ModSecRulesSet.objects.with_id( ObjectId( dataPosted.get( 'modsec_specific_rs_select' + str(cpt), None))) except: specific_rs_rs = ModSecRulesSet.objects.first() specific_rs = ModSecSpecificRulesSet( url=specific_rs_url, rs=specific_rs_rs) specific_rules_set.append(specific_rs) cpt -= 2 # Incoming header management if data.startswith("header_action_"): for cpt in range(dataPosted_length): if data.startswith("header_action_" + str(cpt) + "="): # Force harmless default values to prevent any injection or jQuery problem header_enable = (dataPosted.get( 'header_enable_' + str(cpt), 'off') == 'on') header_action = dataPosted.get( 'header_action_' + str(cpt), 'add') header_name = dataPosted.get('header_name_' + str(cpt), 'Vulture') header_value = dataPosted.get( 'header_value_' + str(cpt), '') header_replacement = dataPosted.get( 'header_replacement_' + str(cpt), '') header_condition = dataPosted.get( 'header_condition_' + str(cpt), 'always') header_condition_v = dataPosted.get( 'header_condition_v_' + str(cpt), '') # FIXME: Coherence control header = HeaderIn(header_enable, header_action, header_name, header_value, header_replacement, header_condition, header_condition_v) incoming_headers.append(header) dataPosted_length -= 7 # Outgoing header management if data.startswith("header_out_action_"): for cpt in range(dataPosted_length): if data.startswith("header_out_action_" + str(cpt) + "="): # Force harmless default values to prevent any injection or jQuery problem header_enable = (dataPosted.get( 'header_out_enable_' + str(cpt), 'off') == 'on') header_action = dataPosted.get( 'header_out_action_' + str(cpt), 'add') header_name = dataPosted.get( 'header_out_name_' + str(cpt), 'Vulture') header_value = dataPosted.get( 'header_out_value_' + str(cpt), '') header_replacement = dataPosted.get( 'header_out_replacement_' + str(cpt), '') header_condition = dataPosted.get( 'header_out_condition_' + str(cpt), 'always') header_condition_v = dataPosted.get( 'header_out_condition_v_' + str(cpt), '') # FIXME: Coherence control header = HeaderOut(header_enable, header_action, header_name, header_value, header_replacement, header_condition, header_condition_v) outgoing_headers.append(header) dataPosted_length -= 7 # # Content rules management if data.startswith("content_types_"): for cpt in range(dataPosted_length): if data.startswith("content_types_" + str(cpt) + "="): # Force harmless default values to prevent any injection # or jQuery problem content_enable = True if dataPosted.get( 'content_enable_' + str(cpt), 'off') == 'on' else False content_types = dataPosted.get( 'content_types_' + str(cpt), 'text/html') content_condition = dataPosted.get( 'condition_' + str(cpt), '') content_deflate = True if dataPosted.get( 'content_deflate_' + str(cpt), 'off') == 'on' else False content_inflate = True if dataPosted.get( 'content_inflate_' + str(cpt), 'off') == 'on' else False content_pattern = dataPosted.get( 'content_pattern_' + str(cpt), '') content_replacement = dataPosted.get( 'content_replacement_' + str(cpt), '') content_replacement_flags = dataPosted.get( 'content_replacement_flags_' + str(cpt), '') # FIXME: Coherence control rule = ContentRule(content_enable, content_types, content_condition, content_deflate, content_inflate, content_pattern, content_replacement, content_replacement_flags) content_rules.append(rule) dataPosted_length -= 8 # SVMs management if data.startswith("checkbox_chart_uri_analysis_"): m = re.match('checkbox_chart_uri_analysis_([0-9|a-f]+)', data) if m: dataset_id_ = m.group(1) # Force harmless default values to prevent any injection or jQuery problem svm_enable = False try: svm_enable = True if dataPosted.get( 'checkbox_chart_uri_analysis_' + dataset_id_) == 'on' else False if svm_enable: svm = SVM.objects(dataset_used=dataset_id_, algo_used="Levenstein")[0] activated_svms.append(str(svm.id)) logger.debug( "Activated SVM with id '{}' for application saved" .format(str(svm.id))) except KeyError: pass except Exception as e: logger.error( "Unable to retrieve activated SVM - Dataset_id:{}, algo_used:Levenstein - Exception: {}" .format(dataset_id_, str(e))) if data.startswith("checkbox_chart_uri_analysis_2_"): m = re.match('checkbox_chart_uri_analysis_2_([0-9|a-f]+)', data) if m: dataset_id_ = m.group(1) # Force harmless default values to prevent any injection or jQuery problem svm_enable = False try: svm_enable = True if dataPosted.get( 'checkbox_chart_uri_analysis_2_' + dataset_id_) == 'on' else False if svm_enable: svm = SVM.objects(dataset_used=dataset_id_, algo_used="Levenstein2")[0] activated_svms.append(str(svm.id)) logger.debug( "Activated SVM with id '{}' for application saved" .format(str(svm.id))) except KeyError: pass except Exception as e: logger.error( "Unable to retrieve activated SVM - Dataset_id:{}, algo_used:Levenstein2 - Exception: {}" .format(dataset_id_, str(e))) if data.startswith("checkbox_chart_bytes_received_"): m = re.match('checkbox_chart_bytes_received_([0-9|a-f]+)', data) if m: dataset_id_ = m.group(1) # Force harmless default values to prevent any injection or jQuery problem svm_enable = False try: svm_enable = True if dataPosted.get( 'checkbox_chart_bytes_received_' + dataset_id_) == 'on' else False if svm_enable: svm = SVM.objects( dataset_used=dataset_id_, algo_used="HTTPcode_bytes_received")[0] activated_svms.append(str(svm.id)) logger.debug( "Activated SVM with id '{}' for application saved" .format(str(svm.id))) except KeyError: pass except Exception as e: logger.error( "Unable to retrieve activated SVM - Dataset_id:{}, algo_used:HTTPcode_bytes_received - Exception: {}" .format(dataset_id_, str(e))) if data.startswith("checkbox_chart_ratio_"): m = re.match('checkbox_chart_ratio_([0-9|a-f]+)', data) if m: dataset_id_ = m.group(1) # Force harmless default values to prevent any injection or jQuery problem svm_enable = False try: svm_enable = True if dataPosted.get( 'checkbox_chart_ratio_' + dataset_id_) == 'on' else False if svm_enable: svm = SVM.objects(dataset_used=dataset_id_, algo_used="Ratio")[0] activated_svms.append(str(svm.id)) logger.debug( "Activated SVM with id '{}' for application saved" .format(str(svm.id))) except KeyError: pass except Exception as e: logger.error( "Unable to retrieve activated SVM - Dataset_id:{}, algo_used:Ratio - Exception: {}" .format(dataset_id_, str(e))) form = ApplicationForm(request.POST, instance=application, listeners=listeners) else: if not object_id: try: rulesset_vulture = [ ModSecRulesSet.objects.get(name="Vulture RS").id ] except ModSecRulesSet.DoesNotExist: rulesset_vulture = [] form = ApplicationForm(initial={ "rules_set": rulesset_vulture, "modsec_policy": ModSec.objects.get(name="Default Policy") }, instance=application) else: form = ApplicationForm( initial={'rules_set': [x.id for x in application.rules_set]}, instance=application) incoming_headers = application.headers_in outgoing_headers = application.headers_out content_rules = application.content_rules specific_rules_set = list() for i in application.specific_rules_set: specific_rules_set.append( ModSecSpecificRulesSet(url=re.sub('^' + application.public_dir, '', i.url), rs=i.rs)) # We replace '\' by '\\' in strings because they're interpreted by templates for tmp_rule in content_rules: tmp_rule.pattern = tmp_rule.pattern.replace("\\", "\\\\") tmp_rule.replacement = tmp_rule.replacement.replace("\\", "\\\\") listeners = application.listeners svms = [ SVM.objects(id=ObjectId(svm_id)).no_dereference().only( 'dataset_used', 'algo_used', 'id').first() for svm_id in application.activated_svms ] # Saving information into database and redirect to application list if request.method == 'POST' and form.is_valid(): # Listener is mandatory if has_listener: old_app = Application.objects.with_id(ObjectId(object_id)) # 1) Remove old listeners, headers and content rules if old_app and old_app.listeners: for listener in old_app.listeners: ip = listener.address.ip port = listener.port n = listener.related_node """ Stop the listener if there is only this app running on it """ if (n, ip, port) not in listeners_ips: logger.info("Stopping listener {}:{}".format(ip, port)) listener.stop() listener.delete() if old_app and old_app.headers_in: for header in old_app.headers_in: header.delete() if old_app and old_app.headers_out: for header in old_app.headers_out: header.delete() if old_app and old_app.content_rules: for rule in old_app.content_rules: rule.delete() if old_app and old_app.specific_rules_set: for ruleset in old_app.specific_rules_set: ruleset.delete() old_cookie_encryption = False old_cookie_cipher = None if old_app: application.wl_bl_rules = old_app.wl_bl_rules old_cookie_encryption = old_app.cookie_encryption old_cookie_cipher = old_app.cookie_cipher old_cookie_cipher_key = old_app.cookie_cipher_key old_cookie_cipher_iv = old_app.cookie_cipher_iv # 2) Create new listeners, headers and content rules for listener in listeners: listener.save() for header in incoming_headers: header.save() for header in outgoing_headers: header.save() for rule in content_rules: rule.save() # 3) Assign listeners, headers and content rules auth_backend = form.cleaned_data.get('auth_backend') auth_backend_fallbacks = form.cleaned_data.get( 'auth_backend_fallbacks') application = form.save(commit=False) if auth_backend: application.auth_backend = auth_backend else: application.auth_backend = None if auth_backend_fallbacks: application.auth_backend_fallbacks = auth_backend_fallbacks else: application.auth_backend_fallbacks = None application.listeners = listeners application.headers_in = incoming_headers application.headers_out = outgoing_headers application.specific_rules_set = specific_rules_set application.content_rules = content_rules application.activated_svms = activated_svms if application.cookie_encryption: if not old_cookie_encryption or not old_cookie_cipher or application.cookie_cipher != old_cookie_cipher: application.cookie_cipher_key = get_random_string( 32 ) if application.cookie_cipher == 'aes256' else get_random_string( 16) application.cookie_cipher_iv = get_random_string( 32 ) if application.cookie_cipher == 'aes256' else get_random_string( 16) else: application.cookie_cipher_key = old_cookie_cipher_key application.cookie_cipher_iv = old_cookie_cipher_iv # 4) Save application if not application.public_dir.endswith('/'): application.public_dir += '/' if application.auth_portal and not application.auth_portal.endswith( '/'): application.auth_portal += '/' if application.private_uri and not application.private_uri.endswith( '/'): application.private_uri += '/' for ruleset in specific_rules_set: ruleset.url = os.path.normpath( str(application.public_dir) + '/' + str(ruleset.url)) ruleset.save() if not object_id: # Create BlackList/WhiteList ModSecRuleSet modsec_wl_bl = ModSecRulesSet( name="{} whitelist/blacklist".format(application.name), type_rule="wlbl") modsec_wl_bl.save() modsec_wl_bl.conf = modsec_wl_bl.get_conf() modsec_wl_bl.save() application.wl_bl_rules = modsec_wl_bl else: # If the application is modified, modify the name of the RS references application.wl_bl_rules.name = "{} whitelist/blacklist".format( form.cleaned_data.get('name')) application.wl_bl_rules.save() for modsec_ruleset in ModSecRulesSet.objects.filter( name="Learning {} WL"): modsec_ruleset.name = form.cleaned_data.get('name') modsec_ruleset.save() if application.type == "balanced": application.private_uri = "{}://{}".format( application.proxy_balancer.members[0].uri_type, application.proxy_balancer.members[0].uri) # Check if api_call to reload rsyslogd is needed if old_app: if application.log_custom != old_app.log_custom or application.log_level != old_app.log_level or application.learning != old_app.learning: application.save() else: application.save(no_apicall=True) else: application.save() return HttpResponseRedirect('/application/') inets = Listener.objects() address_list = list() vhid_list = list() # List and categorize inet (carp or not) to render them in template for inet in inets: listener = dict() if inet.is_carp: listener['inet'] = inet listener['id'] = getattr(inet, 'id') vhid_list.append(inet.carp_vhid) elif inet.carp_vhid in vhid_list: continue else: listener['inet'] = inet listener['id'] = getattr(inet, 'id') address_list.append(listener) ssl_profile_list = ModSSL.objects() rules_set_list = ModSecRulesSet.objects.filter( type_rule__in=['crs', 'trustwave', 'vulture', 'custom']) return render_to_response('application_edit.html', { 'form': form, 'object_id': object_id, 'headers_in': incoming_headers, 'headers_out': outgoing_headers, 'content_rules': content_rules, 'listeners': listeners, 'address_list': address_list, 'ssl_profile_list': ssl_profile_list, 'application': application, 'svms': svms, 'rules_set_list': rules_set_list, 'specific_rules_set': specific_rules_set }, context_instance=RequestContext(request))
def perform_action(self, request, old_password): new_passwd = request.POST['password_1'] new_passwd_cfrm = request.POST['password_2'] rdm = (request.GET.get("rdm", None) or request.POST.get('rdm', None)) # If not rdm : Verify password if not rdm: saved_app_id = self.redis_portal_session.keys['app_id_' + str(self.backend.id)] saved_app = Application.objects(id=ObjectId(saved_app_id)).only( 'id', 'name', 'pw_min_len', 'pw_min_upper', 'pw_min_lower', 'pw_min_number', 'pw_min_symbol').first() if not self.redis_portal_session.getAutologonPassword( str(saved_app.id), str(self.backend.id), self.username): raise AuthenticationError("Wrong old password") else: saved_app = self.application #Check if password meets required complexity upper_case = 0 lower_case = 0 number = 0 symbol = 0 min_len = int(saved_app.pw_min_len) min_upper = int(saved_app.pw_min_upper) min_lower = int(saved_app.pw_min_lower) min_number = int(saved_app.pw_min_number) min_symbol = int(saved_app.pw_min_symbol) for i in new_passwd: if i.isupper(): upper_case += 1 elif i.islower(): lower_case += 1 elif i.isdigit(): number += 1 else: symbol += 1 if not (len(new_passwd) >= min_len and upper_case >= min_upper and lower_case >= min_lower and number >= min_number and symbol >= min_symbol): logger.info("SELF::change_password: Password is too weak") raise AuthenticationError( "Password do not meet complexity requirements") if issubclass(self.backend.__class__, Backend): self.backend.change_password( self.username, old_password, new_passwd, krb5_service=self.application.app_krb_service) elif isinstance(self.backend.get_backend(), MongoEngineBackend): user = User.objects.get(username=str(self.username)) new_password_hash = make_password(new_passwd) user.password = new_password_hash user.save() else: self.backend.get_backend().change_password( self.username, old_password, new_passwd, krb5_service=self.application.app_krb_service) logger.info( "SELF::change_password: Password successfully changed in backend") # If not rdm : set new password in Redis portal session if not rdm: if self.redis_portal_session.setAutologonPassword( str(saved_app.id), str(saved_app.name), str( self.backend.id), self.username, old_password, new_passwd) is None: # If setAutologonPasswd return None : the old_password was incorrect raise AuthenticationError("Wrong old password") logger.info( "SELF::change_password: Password successfully changed in Redis" ) return "Password successfully changed"
GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Vulture 3. If not, see http://www.gnu.org/licenses/. """ __author__ = "Olivier de Régis" __credits__ = [] __license__ = "GPLv3" __version__ = "3.0.0" __maintainer__ = "Vulture Project" __email__ = "*****@*****.**" __doc__ = """This migration script update the auth_backend_fallback to a list""" import os import sys sys.path.append('/home/vlt-gui/vulture') os.environ.setdefault("DJANGO_SETTINGS_MODULE", 'vulture.settings') import django django.setup() from gui.models.application_settings import Application if __name__ == '__main__': applications = Application.objects() for app in applications: app.auth_backend_fallbacks = [app.auth_backend_fallback] app.save()
def clean(self): super(ApplicationForm, self).clean() if self.cleaned_data.get('type') == 'balanced' \ and not self.cleaned_data.get('proxy_balancer'): self.add_error('proxy_balancer', 'Please choose a proxy balancer') if self.cleaned_data.get('need_auth') and self.cleaned_data.get( 'auth_type' ) == "form" and not self.cleaned_data.get('template'): self.add_error( 'template', 'Please choose a template for authentication forms') if self.cleaned_data.get( 'public_name') and '/' in self.cleaned_data.get('public_name'): self.add_error('public_name', 'Public FQDN cannot contain "/"') if self.cleaned_data.get( 'public_alias') and '/' in self.cleaned_data.get( 'public_alias'): self.add_error('public_alias', 'Public FQDN cannot contain "/"') if ' ' in self.cleaned_data.get('public_dir'): self.add_error('public_dir', 'Public dir must not contain space') if self.cleaned_data.get('auth_type') != 'form': self.cleaned_data['otp_repository'] = None # It is not possible to set auth_type AND sso_type to 'kerberos' if self.cleaned_data.get( 'auth_type') == 'kerberos' and self.cleaned_data.get( 'sso_forward') == 'kerberos': self.add_error( 'auth_type', " You cannot set authentication type AND sso forward type to 'kerberos'" ) self.add_error( 'sso_forward', " You cannot set authentication type AND sso forward type to 'kerberos'" ) loadbalancers = [ "{}:{}".format(lb.incoming_listener.ip, lb.incoming_port) for lb in Loadbalancer.objects.all() ] for listener in self.listeners: if "{}:{}".format(listener.address.ip, listener.port) in loadbalancers: self.add_error( 'Network', " An haproxy configuration is currently using this port with this Listener" ) # If sso_forward == "kerberos" -> verify if auth_backend(fallback) is a KerberosRepository error = True if self.cleaned_data.get('sso_forward') == 'kerberos': for repo in BaseAbstractRepository.get_auth_repositories(): if str(self.cleaned_data.get('auth_backend')) == str( repo.id) and isinstance(repo.get_backend(), KerberosBackend): error = False break elif str( self.cleaned_data.get('auth_backend_fallback')) == str( repo.id) and isinstance(repo.get_backend(), KerberosBackend): error = False break if error: self.add_error( 'sso_forward', "If sso_forward = 'Kerberos', you must have a kerberos backend" ) auth_type = [x[0] for x in Application().AUTH_TYPE] if self.cleaned_data.get('sso_enabled') and self.cleaned_data.get( 'auth_type') not in auth_type: self.add_error( 'auth_type', "Please select a forward type for the SSO Forward configuration" ) # If there is a modsec rulesSet selected if self.cleaned_data.get('rules_set', None): # And a ModSecPolicy with 'body_inspection' if Off modsec_policy = self.cleaned_data.get('modsec_policy', None) if not hasattr(modsec_policy, 'seccontentinjection' ) or not modsec_policy.seccontentinjection: self.add_error( 'rules_set', 'The ModSec Policy must have "Content injection" to On to activate ModSec RulesSet' ) self.cleaned_data["custom_location"] = self.cleaned_data.get( 'custom_location', "").replace("\r", "") self.cleaned_data["custom_vhost"] = self.cleaned_data.get( 'custom_vhost', "").replace("\r", "") self.cleaned_data["custom_proxy"] = self.cleaned_data.get( 'custom_proxy', "").replace("\r", "") return self.cleaned_data
def log_in(request, token_name=None, token=None, proxy_app_id=None): """ Handle authentication in Vulture Portal :param request: Django request object :returns: Home page if user auth succeed. Logon page if auth failed """ default_authentication_type = None cluster = Cluster.objects.get() """ Check if URI arguments are valid """ if token_name and token_name != cluster.getTokenName(): logger.info("PORTAL::Authentication: Invalid token in URI " + str(token_name) + '/' + str(token)) return HttpResponseForbidden() authentication_classes = { 'form': POSTAuthentication, 'basic': BASICAuthentication, 'kerberos': KERBEROSAuthentication } """ Retrieve token and cookies to instantiate Redis wrapper objects """ # Retrieve cookies required for authentication portal_cookie_name = cluster.getPortalCookie() portal_cookie = request.COOKIES.get(portal_cookie_name, None) app_cookie = request.COOKIES.get(cluster.getAppCookie(), None) try: # Instantiate authentication object to retrieve application auth_type authentication = Authentication(token_name, token, app_cookie, portal_cookie) # And then instantiate the right authentication class with auth_type ('form','basic','kerberos') authentication = authentication_classes[ authentication.application.auth_type](token_name, token, app_cookie, portal_cookie) logger.debug("PORTAL::log_in: Authentication successfully created") # Application does not need authentication except RedirectionNeededError as e: logger.error("PORTAL::log_in: {}".format(str(e))) return HttpResponseRedirect(e.redirect_url) # Redis connection error except RedisConnectionError as e: logger.error( "PORTAL::log_in: Unable to connect to Redis server : {}".format( str(e))) return response_failure(HttpResponseServerError(), "authentication") # Token not found while instantiating RedisSession or RedisAppSession except TokenNotFoundError as e: logger.error("PORTAL::log_in: {}".format(str(e))) try: # Retrieve application object to redirect to application default uri application = Application.objects(id=ObjectId(proxy_app_id)).only( 'listeners', 'public_name', 'public_dir').first() return HttpResponseRedirect(application.get_redirect_uri()) # If "proxy_app_id" not found : FORBIDDEN except (Application.DoesNotExist, InvalidId, ValidationError) as e: logger.error( "PORTAL::log_in: Application with id '{}' not found : {}". format(proxy_app_id, str(e))) return response_failure(HttpResponseForbidden(), "authentication") # If redis_session.keys['application_id'] does not exists : FORBIDDEN except (Application.DoesNotExist, ValidationError, InvalidId) as e: logger.error( "PORTAL::log_in: Application with id '{}' not found".format( authentication.redis_session.keys['application_id'])) return response_failure(HttpResponseForbidden(), "authentication") # If assertionError : Ask credentials by portal except AssertionError as e: logger.error( "PORTAL::log_in: AssertionError while trying to create Authentication : " .format(e)) return authentication.ask_credentials_response(request=request) """ If user is not authenticated : try to retrieve credentials and authenticate him on backend/fallback-backends """ # If the user is not authenticated and application need authentication if not authentication.is_authenticated(): default_authentication_type = "authentication" try: backend_id = authentication.authenticate_on_backend() if not backend_id: # Retrieve credentials authentication.retrieve_credentials(request) logger.debug( "PORTAL::log_in: Credentials successfully retrieved") # Authenticate user with credentials retrieven authentication_results = authentication.authenticate(request) logger.debug( "PORTAL::log_in: Authentication succeed on backend {}". format(authentication.backend_id)) # Register authentication results in Redis app_cookie, portal_cookie, oauth2_token = authentication.register_user( authentication_results) logger.debug( "PORTAL::log_in: User {} successfully registered in Redis". format(authentication.credentials[0])) if authentication_results['data'].get('password_expired', None): logger.info( "PORTAL::log_in: User '{}' must change its password, redirect to self-service portal" .format(authentication.credentials[0])) app_url = authentication.get_url_portal() return response_success( response_redirect_with_portal_cookie( app_url + str(token_name) + '/self/change', portal_cookie_name, portal_cookie, app_url.startswith('https'), None), "authentication") # If the user is already authenticated (retrieven with RedisPortalSession ) => SSO else: app_cookie, portal_cookie, oauth2_token = authentication.register_sso( backend_id) logger.info( "PORTAL::log_in: User {} successfully SSO-powered !". format(authentication.credentials[0])) except AssertionError as e: logger.error( "PORTAL::log_in: Bad captcha taped for username '{}' : {}". format(authentication.credentials[0], e)) return response_failure( authentication.ask_credentials_response(request=request, error="Bad captcha"), "authentication") except AccountLocked as e: logger.error( "PORTAL::log_in: Error while trying to authenticate user '{}' : {}" .format(authentication.credentials[0], e)) return response_failure( authentication.ask_credentials_response( request=request, error="Bad credentials"), "authentication") except AuthenticationError as e: logger.error( "PORTAL::log_in: AuthenticationError while trying to authenticate user '{}' : {}" .format(authentication.credentials[0], e)) return response_failure( authentication.ask_credentials_response( request=request, error="Bad credentials"), "authentication") except ACLError as e: logger.error( "PORTAL::log_in: ACLError while trying to authenticate user '{}' : {}" .format(authentication.credentials[0], e)) return response_failure( authentication.ask_credentials_response( request=request, error="Bad credentials"), "authentication") except (DBAPIError, PyMongoError, LDAPError) as e: logger.error( "PORTAL::log_in: Repository driver Error while trying to authentication user '{}' : {}" .format(authentication.credentials[0], e)) return response_failure( authentication.ask_credentials_response( request=request, error="Bad credentials"), "authentication") except (MultiValueDictKeyError, AttributeError, KeyError) as e: #vltprtlsrnm is always empty during the initial redirection. Don't log that logger.debug( "PORTAL::log_in: Error while trying to authentication user '{}' : {}" .format(authentication.credentials[0], e)) return authentication.ask_credentials_response(request=request) except REDISWriteError as e: logger.error( "PORTAL::log_in: RedisWriteError while trying to register user '{}' informations : {}" .format(authentication.credentials[0], e)) return response_failure(HttpResponseServerError(), "authentication") except Exception as e: logger.exception(e) return response_failure(HttpResponseServerError(), "authentication") """ If user is not double-authenticated and double-authentication needed : try to retrieve credentials and authenticate him on otp-backend """ # If the user is authenticated but not double-authenticated and double-authentication required if authentication.double_authentication_required(): default_authentication_type = "otp" logger.info( "PORTAL::log_in: Double authentication required for user '{}'". format(authentication.credentials[0])) try: # Instantiate DOUBLEAuthentication object db_authentication = DOUBLEAuthentication(cluster.getTokenName(), token, app_cookie, portal_cookie) logger.debug( "PORTAL::log_in: DoubleAuthentication successfully created") # And try to retrieve credentials db_authentication.retrieve_credentials(request) logger.debug( "PORTAL::log_in: DoubleAuthentication credentials successfully retrieven" ) # And use them to authenticate user db_authentication.authenticate(request) logger.info( "PORTAL::log_in: User '{}' successfully double authenticated". format(authentication.credentials[0])) except AssertionError as e: """ If redis_portal_session does not exists or can't retrieve otp key in redis """ logger.error( "PORTAL::log_in: DoubleAuthentication failure for username '{}' : {}" .format(authentication.credentials[0], str(e))) return response_failure( authentication.ask_credentials_response( request=request, portal_cookie_name=portal_cookie_name, error="Portal cookie expired"), "otp") except (Application.DoesNotExist, ValidationError, InvalidId) as e: """ Invalid POST 'vulture_two_factors_authentication' value """ logger.error( "PORTAL::log_in: Double-authentication failure for username {} : {}" .format(authentication.credentials[0], str(e))) return response_failure( HttpResponseForbidden("Intrusion attempt blocked"), "otp") except REDISWriteError as e: """ Cannot register double-authentication in Redis : internal server error """ logger.error( "PORTAL::log_in: Failed to write double-authentication results in Redis for username '{}' : {}" .format(db_authentication.credentials[0], str(e))) return response_failure(HttpResponseServerError(), "otp") # If authentication failed : create double-authentication key and ask-it except CredentialsError as e: """ CredentialsError: no OTP credentials provided : ask-them """ logger.error( "PORTAL::log_in: Double-authentication failure for username {} : {}" .format(authentication.credentials[0], str(e))) try: db_authentication.create_authentication() # If we get here, authentication has succeed return response_success( db_authentication.ask_credentials_response( request=request, portal_cookie_name=portal_cookie_name), "authentication") except (OTPError, REDISWriteError, RedisConnectionError) as e: """ Error while sending/registering in Redis the OTP informations : display portal""" logger.error( "PORTAL::log_in: Failed to create/send double-authentication key : {}" .format(str(e))) db_authentication.deauthenticate_user() logger.info( "PORTAL::log_in: User '{}' successfully deauthenticated due to db-authentication error" .format(authentication.credentials[0])) return response_failure( authentication.ask_credentials_response( request=request, error="<b> Error sending OTP Key </b> </br> " + str(e)), "otp") except AuthenticationError as e: """ Bad OTP key """ logger.error( "PORTAL::log_in: DoubleAuthentication failure for username {} : {}" .format(authentication.credentials[0], str(e))) try: db_authentication.create_authentication() db_authentication.authentication_failure() logger.debug( "PORTAL:log_in: DoubleAuthentication failure successfully registered in Redis" ) return response_failure( db_authentication.ask_credentials_response( request=request, portal_cookie_name=portal_cookie_name, error="<b> Bad OTP key </b>"), "otp") except TwoManyOTPAuthFailure as e: logger.error( "PORTAL::log_in: Two many OTP authentication failures for username'{}', redirecting to portal" .format(authentication.credentials[0])) db_authentication.deauthenticate_user() logger.info( "PORTAL::log_in: User '{}' successfully deauthenticated due to db-authentication error" .format(authentication.credentials[0])) return response_failure( authentication.ask_credentials_response(request=request, error=e.args[0]), "otp") except (OTPError, REDISWriteError, RedisConnectionError) as e: logger.error( "PORTAL::log_in: Error while preparing double-authentication : {}" .format(str(e))) return response_failure( db_authentication.ask_credentials_response( request=request, portal_cookie_name=portal_cookie_name, error="<b> Error sending OTP Key </b> </br> " + str(e)), "otp") except OTPError as e: """ OTP Error while authenticating given token """ logger.error( "PORTAL::log_in: Double-authentication failure for username {} : {}" .format(authentication.credentials[0], str(e))) return response_failure( db_authentication.ask_credentials_response( request=request, portal_cookie_name=portal_cookie_name, error="<b> OTP Error </b> {}".format(str(e))), "otp") except TwoManyOTPAuthFailure as e: logger.error( "PORTAL::log_in: Two many OTP authentication failures for username'{}', redirecting to portal" .format(authentication.credentials[0])) db_authentication.deauthenticate_user() logger.info( "PORTAL::log_in: User '{}' successfully deauthenticated due to db-authentication error" .format(authentication.credentials[0])) return response_failure( authentication.ask_credentials_response(request=request, error=e.args[0]), "otp") # If we arrive here : the user is authenticated # and double-authenticated if double-authentication needed sso_methods = { 'form': SSOForwardPOST, 'basic': SSOForwardBASIC, 'kerberos': SSOForwardKERBEROS } """ If SSOForward enabled : perform-it """ if authentication.application.sso_enabled: # Try to retrieve credentials from authentication object try: if not authentication.credentials[ 0] or not authentication.credentials[1]: authentication.get_credentials(request) # If we cannot retrieve them, ask credentials if not authentication.credentials[ 0]: # or not authentication.credentials[1]: # If we get here, otp or auth has succeed return response_success( authentication.ask_credentials_response( request=request, portal_cookie_name=portal_cookie_name, error="Credentials not found"), default_authentication_type) logger.info( "PORTAL::log_in: Credentials successfuly retrieven for SSO performing" ) except Exception as e: logger.error( "PORTAL::log_in: Error while retrieving credentials for SSO : " ) logger.exception(e) return response_success( authentication.ask_credentials_response( request=request, portal_cookie_name=portal_cookie_name, error="Credentials not found"), default_authentication_type) try: # Instantiate SSOForward object with sso_forward type sso_forward = sso_methods[authentication.application.sso_forward]( request, authentication.application, authentication) logger.info("PORTAL::log_in: SSOForward successfully created") # Get credentials needed for sso forward : AutoLogon or Learning sso_data, profiles_to_stock, url = sso_forward.retrieve_credentials( request) logger.info( "PORTAL::log_in: SSOForward credentials successfully retrieven" ) # If credentials retrieven needs to be stocked for profile_name, profile_value in profiles_to_stock.items(): sso_forward.stock_sso_field(authentication.credentials[0], profile_name, profile_value) # Use 'sso_data' and 'url' to authenticate user on application response = sso_forward.authenticate( sso_data, post_url=url, redis_session=authentication.redis_session) logger.info("PORTAL::log_in: SSOForward performing success") # Generate response depending on application.sso_forward options final_response = sso_forward.generate_response( request, response, authentication.get_redirect_url()) logger.info( "PORTAL::log_in: SSOForward response successfuly generated") # If the user has not yet a portal cookie : give-it if not request.COOKIES.get( portal_cookie_name, None) or not authentication.redis_base.hgetall( request.COOKIES.get(portal_cookie_name, None)): final_response = set_portal_cookie( final_response, portal_cookie_name, portal_cookie, authentication.get_redirect_url()) return response_success(final_response, default_authentication_type) # If learning credentials cannot be retrieven : ask them except CredentialsMissingError as e: logger.error( "PORTAL::log_in: Learning credentials missing : asking-them") return response_success( authentication.ask_learning_credentials( request=request, portal_cookie_name=None if request.POST.get( portal_cookie_name, None) else portal_cookie_name, fields=e.fields_missing), default_authentication_type) # If KerberosBackend object cannot be retrieven from mongo with the backend_id that the user is authenticated on except InvalidId: logger.error( "PORTAL::log_in: The user is authenticated on a not Kerberos backend, cannot do SSOForward" ) except (RequestsConnectionError, OpenSSLError) as e: logger.error( "PORTAL::log_in: ConnectionError while trying to SSO to backend : " ) logger.exception(e) except Exception as e: logger.error( "PORTAL::log_in: Unexpected error while trying to perform SSO Forward :" ) logger.exception(e) """ If no response has been returned yet : redirect to the asked-uri/default-uri with portal_cookie """ redirection_url = authentication.get_redirect_url() logger.info( "PORTAL::log_in: Redirecting user to '{}'".format(redirection_url)) try: kerberos_token_resp = authentication_results['data']['token_resp'] except: kerberos_token_resp = None return response_success( response_redirect_with_portal_cookie( redirection_url, portal_cookie_name, portal_cookie, redirection_url.startswith('https'), kerberos_token_resp), default_authentication_type)
def post(self, request, app_id, login=""): if not login: return JsonResponse({'status': False, 'error': "Login missing."}) try: app = Application.objects( id=ObjectId(app_id)).no_dereference().only( 'name', 'auth_backend', 'sso_profile').first() if not app: raise Application.DoesNotExist() assert app.sso_profile, "No wizard SSO configured for the application '{}'".format( app.name) except (Application.DoesNotExist, InvalidId) as e: logger.error( "SSOProfiles::GET: Application with id '{}' not found".format( app_id)) return JsonResponse({ 'status': False, 'error': "Application with id '{}' not found.".format(app_id) }) except AssertionError as e: return JsonResponse({'status': False, 'error': str(e)}) try: # First check if that user is in repository if not self.check_user_exists(app, login): return JsonResponse({ 'status': False, 'error': "User '{}' not found " "in repository '{}'".format(login, app.getAuthBackend()) }) # Verify if a Profile already exists if SSOProfile.objects.filter(app_id=str(app.id), repo_id=str(app.getAuthBackend().id), login=login).count() != 0: return JsonResponse({ 'status': False, 'error': "SSOProfile already exists." }) # Retrieve the list of application sso_profiles sso_profiles_app = json_loads(app.sso_profile) # And verify if all fields are provided request_data = request.POST or request.JSON new_sso_profiles = {} unknown_fields = list(request_data.keys()) for sso_profile_app in sso_profiles_app: if sso_profile_app['type'] in ("learn", "learn_secret"): field_name = sso_profile_app['name'].split(';vlt;')[0] # First try to retrieve the friendly name. if sso_profile_app.get('asked_name') and request_data.get( sso_profile_app.get('asked_name')): field_value = request_data.get( sso_profile_app.get('asked_name')) unknown_fields.remove( sso_profile_app.get('asked_name')) else: field_value = request_data.get(field_name) if field_name in unknown_fields: unknown_fields.remove(field_name) assert field_value, "Field named '{}' {} missing.".format( field_name, "friendly name '{}'".format( sso_profile_app.get('asked_name')) if sso_profile_app.get('asked_name') else "") new_sso_profiles[field_name] = field_value # Check if a field provided is not in application's sso_profiles if unknown_fields: # Correctly format fields for error if len(unknown_fields) == 1: unknown_fields = unknown_fields.pop() else: unknown_fields = ",".join(unknown_fields) raise Exception( "Field(s) '{}' not found in Application learning fields.". format(unknown_fields)) app.set_sso_profiles(login, new_sso_profiles) return JsonResponse({'status': True}) except Exception as e: logger.critical(e, exc_info=1) return JsonResponse({'status': False, 'error': str(e)})
def profile_edit(request, app_id, login): try: app = Application.objects(id=ObjectId(app_id)).no_dereference().only( 'name', 'auth_backend', 'sso_profile', 'sso_forward').first() except (Application.DoesNotExist, InvalidId) as e: logger.error( "SSOProfiles::GET: Application with id '{}' not found".format( app_id)) return HttpResponseForbidden( "Application with id '{}' not found.".format(app_id)) # Check if user exists ? if app.sso_forward == "basic": sso_profiles_app = [{ 'type': "learn", 'name': "basic_username;vlt;", 'asked_name': "username" }, { 'type': "learn_secret", 'name': "basic_password;vlt;", 'asked_name': "password" }] elif app.sso_forward == "kerberos": sso_profiles_app = [{ 'type': "learn", 'name': "kerberos_username;vlt;", 'asked_name': "username" }, { 'type': "learn_secret", 'name': "kerberos_password;vlt;", 'asked_name': "password" }] else: # Retrieve the list of application sso_profiles try: sso_profiles_app = json_loads(app.sso_profile) except: raise Exception( "No SSO wizard configured for this application. Cannot retrieve user profiles." ) errors = [] profiles = [] try: if request.method == "POST": # And verify if all fields are provided request_data = request.POST user_profiles = {} for sso_profile_app in sso_profiles_app: if sso_profile_app['type'] in ("learn", "learn_secret"): field_name, field_id = sso_profile_app['name'].split( ';vlt;') field_value = request_data.get(field_name) # If a learning_secret field has not been modified, no need to update it if field_value == "*" * len(field_value): continue elif field_value is None: errors.append( "Field named '{}' missing.".format(field_name)) else: user_profiles[field_name] = field_value # Check if a field provided is not in application's sso_profiles unknown_fields = list( set(request_data.keys()).difference(set(user_profiles.keys()))) if not unknown_fields: errors.append( "Field(s) {} not found in Application learning fields". format(",".join(unknown_fields))) if not errors: app.set_sso_profiles(login, user_profiles) return HttpResponseRedirect("/repository/sso_profiles") else: user_profiles = app.get_sso_profiles(login) # Get application sso wizard, in case some field(s) has been added for profile in sso_profiles_app: if profile['type'] in ("learn", "learn_secret"): profile_name = profile['name'].split(";vlt;")[0] if profile.get('asked_name'): profile_value = user_profiles.get( profile.get('asked_name')) if not profile_value: profile_value = user_profiles.get(profile_name, "") profiles.append( (profile_name, profile.get("asked_name", "Not field"), profile_value, profile['type'])) except Exception as e: logger.critical(e, exc_info=1) errors = [str(e)] return render_to_response("sso_profiles_edit.html", { 'profiles': profiles, 'app_name': app.name, 'repository': app.getAuthBackend().repo_name, 'login': login, 'error': errors }, context_instance=RequestContext(request))
def traffic(request): apps = [] for app in Application.objects(): if app.log_custom.repository_type == 'data': apps.append(app) if not request.is_ajax(): cluster = Cluster.objects.get() loganalyser_settings = cluster.system_settings.loganalyser_settings rules = loganalyser_settings.loganalyser_rules tags = [] for rule in rules: tags.extend(rule.tags.split(',')) return render_to_response('monitor_traffic.html', {'apps': apps, 'tags': set(tags)}, context_instance=RequestContext(request)) codes = json.loads(request.POST['codes']) apps_id = json.loads(request.POST['apps_id']) tags = json.loads(request.POST['tags']) if apps_id is not None: repos = {} for app_id in apps_id: app = Application.objects.with_id(ObjectId(app_id)) try: repos[app.log_custom.repository].append(app) except: repos[app.log_custom.repository] = [app] else: repos = {} for app in apps: try: repos[app.log_custom.repository].append(app) except: repos[app.log_custom.repository] = [app] now = datetime.datetime.utcnow() before = now - datetime.timedelta(minutes=10) params = { 'codes' : codes, 'tags' : tags, 'startDate': before, 'endDate' : now } results, max_n = {}, 0 for repo, apps in repos.items(): params['apps'] = apps if repo.type_uri == 'mongodb': client = MongoDBClient(repo) elif repo.type_uri == 'elasticsearch': client = ElasticSearchClient(repo) for key, value in client.map(params).items(): try: results[key] += value except KeyError: results[key] = value if value > max_n: max_n = value return JsonResponse({ 'results': results, 'max' : max_n })
def check_config_changes (sender, id, **kwargs): """ Receiver of config_modified signal. This checks if the Vulture's Apache configuration files needs to be refreshed :param sender: :param kwargs: :return: """ from gui.models.application_settings import Application, ListenAddress from gui.models.modssl_settings import ModSSL from gui.models.rewrite_settings import Rewrite """ According to sender name, find Applications using the sender's Class """ if sender.__name__ == "ModLog": app_list = Application.objects.filter(log_custom=ObjectId(id)) elif sender.__name__ == "ModAccess": app_list = Application.objects.filter(access_mode=ObjectId(id)) elif sender.__name__ == 'portalTemplate': listeners, tmp = [], [] for app in Application.objects.filter(template=ObjectId(id)): for l in app.listeners: if "{}:{}".format(l.address.ip, l.port) not in tmp: l.is_up2date = False l.save() tmp.append("{}:{}".format(l.address.ip, l.port)) return None elif sender.__name__ == "SSLCertificate" or sender.__name__ == "ModSSL": if sender.__name__ == "SSLCertificate": ssl_profile = ModSSL.objects.filter(certificate=ObjectId(id)) listeners = list() for profile in ssl_profile: ls = ListenAddress.objects.filter(ssl_profile=profile) for l in ls: if l not in listeners: listeners.append(l) else: listeners = ListenAddress.objects.filter(ssl_profile=ObjectId(id)) """ Job's done: check listeners """ done = list() for listener in listeners: key = listener.address.ip + ':' + listener.port if key not in done: print("Dealing with listener " + key) listener.need_restart() done.append(key) return None elif sender.__name__ == "ModSec": app_list = Application.objects.filter(modsec_policy=ObjectId(id)) elif sender.__name__ == "ModSecRulesSet": app_list = Application.objects.filter(Q(rules_set=ObjectId(id)) or Q(wlbl=ObjectId(id))) elif sender.__name__ == "ProxyBalancer": app_list = Application.objects.filter(proxy_balancer=ObjectId(id)) elif sender.__name__ == "Rewrite": app_list = None try: app_list = Rewrite.objects.get(id=ObjectId(id)).application except: pass """ This is a rule that apply to all applications """ if not app_list: app_list = Application.objects() elif sender.__name__ == "Worker": app_list = Application.objects.filter(worker=ObjectId(id)) elif sender.__name__ == "Cluster": app_list = Application.objects.all() else: print("Signal::check_config_changes(): Sender is unknown: " + str(sender.__name__)) return None done = list() for app in app_list: for listener in app.listeners: key = listener.address.ip+':'+str(listener.port) if key not in done: print("Dealing with listener " + key) listener.need_restart() done.append(key)
def report_data(request): daterange = json.loads(request.POST['daterange']) params = { 'startDate': daterange['startDate'], 'endDate': daterange['endDate'], 'reporting_type': request.POST['reporting_type'] } errors = [] if request.POST['reporting_type'] in ('access', 'security'): apps = [] apps_id = json.loads(request.POST['apps']) if apps_id is not None: repos = {} for app_id in apps_id: app = Application.objects.with_id(ObjectId(app_id)) try: repos[app.log_custom.repository].append(app) except: repos[app.log_custom.repository] = [app] else: for app in Application.objects(): if app.log_custom.repository_type == 'data': apps.append(app) repos = {} for app in apps: try: repos[app.log_custom.repository].append(app) except: repos[app.log_custom.repository] = [app] params['type_logs'] = 'access' results = {} for repo, apps in repos.items(): params['apps'] = apps try: if repo.type_uri == 'mongodb': client = MongoDBClient(repo) elif repo.type_uri == 'elasticsearch': client = ElasticSearchClient(repo) aggregation = client.aggregate(params) if results: results = client.merge_aggregations(aggregation, results) else: results = aggregation except ClientLogException as e: errors.append(str(e)) results = client.fill_data(results) elif request.POST['reporting_type'] == 'packet_filter': node_id = request.POST['node'] results = {} node = Node.objects.with_id(ObjectId(node_id)) repo = node.system_settings.pf_settings.repository params['type_logs'] = 'packet_filter' params['node'] = node.name try: if repo.type_uri == 'mongodb': client = MongoDBClient(repo) results = client.aggregate(params) elif repo.type_uri == 'elasticsearch': client = ElasticSearchClient(repo) results = client.aggregate(params) except ClientLogException as e: errors.append(str(e)) results = client.fill_data(results) return JsonResponse({'results': results, 'errors': errors})