def update(self): """Check the current state of the rule in pfSense""" import pprint, sys from PfsenseFauxapi.PfsenseFauxapi import PfsenseFauxapi _LOGGER.debug("Getting pfSense current rule state for %s", self._rule_name) try: # Setup connection with devices/cloud FauxapiLib = PfsenseFauxapi(self._host, self._api_key, self._access_token, debug=True) # Get the current set of filters filters = FauxapiLib.config_get('filter') for rule in filters['rule']: if (rule.get('tracker') == self._tracker_id): _LOGGER.debug( "Found rule with tracker %s, updating state.", self._tracker_id) if ('disabled' in rule): self._state = False else: self._state = True except: _LOGGER.error( "Problem retrieving rule set from pfSense host: %s. Likely due to API key or secret.", self._host)
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._fapi = PfsenseFauxapi( self.config['api_host'], self.config['key'], self.config['secret'], )
def __init__(self, fauxapi_host, fauxapi_apikey, fauxapi_apisecret, debug=False): self.FauxapiLib = PfsenseFauxapi(fauxapi_host, fauxapi_apikey, fauxapi_apisecret, debug)
def test_fauxapi_not_installed(mock_requests_get): fauxapi = PfsenseFauxapi(host=None, apikey=None, apisecret=None) mock_requests_get.return_value.status_code = 404 with pytest.raises(Exception) as exception_info: fauxapi.config_get() assert 'Unable to find FauxAPI on target host' in str(exception_info.value)
def test_config_get(mock_requests_get): fauxapi = PfsenseFauxapi(host=None, apikey=None, apisecret=None) mock_requests_get.return_value.status_code = 200 mock_requests_get.return_value.text = '{"callid": "b905cb4ef8db8", "action": "config_get", "message": "ok", "data": { "config": {"version": "18.9", "lastchange": "", "system": {}} }}' response = fauxapi.config_get() assert response['version'] == '18.9'
def setup_platform(hass, config, add_entities, discovery_info=None): """Initialize the platform""" """Setup the pfSense Rules platform.""" import pprint, sys from PfsenseFauxapi.PfsenseFauxapi import PfsenseFauxapi # Assign configuration variables. The configuration check takes care they are # present. host = config.get(CONF_HOST) api_key = config.get(CONF_API_KEY) access_token = config.get(CONF_ACCESS_TOKEN) rule_prefix = config.get(CONF_RULE_FILTER) _LOGGER.debug( "Connecting to pfSense firewall to collect rules to add as switches.") try: FauxapiLib = PfsenseFauxapi(host, api_key, access_token, debug=True) # Get the current set of filters filters = FauxapiLib.config_get('filter') _LOGGER.debug("Found %s rules in pfSense", len(filters['rule'])) if rule_prefix: _LOGGER.debug("Filter for rules starting with %s being applied", rule_prefix) rules = [] # Iterate through and find rules i = 0 for rule in filters['rule']: tracker = rule.get('tracker') if tracker == None: _LOGGER.warning("Skipping rule (no tracker_id): " + rule['descr']) else: if rule_prefix: if (rule['descr'].startswith(rule_prefix)): _LOGGER.debug("Found rule %s", rule['descr']) new_rule = pfSense('pfsense_' + rule['descr'], rule['descr'], tracker, host, api_key, access_token) rules.append(new_rule) else: _LOGGER.debug("Found rule %s", rule['descr']) new_rule = pfSense('pfsense_' + rule['descr'], rule['descr'], tracker, host, api_key, access_token) rules.append(new_rule) i = i + 1 # Add devices add_entities(rules) except Exception as e: _LOGGER.error( "Problem getting rule set from pfSense host: %s. Likely due to API key or secret. More Info:" + str(e), host)
def test_function_call(mock_requests_post): fauxapi = PfsenseFauxapi(host=None, apikey=None, apisecret=None) mock_requests_post.return_value.status_code = 200 mock_requests_post.return_value.text = '{"callid": "5c8dc7cda6948", "action": "function_call", "message": "ok", "data": {"return": {}}}' response = fauxapi.function_call(None) assert response['action'] == 'function_call' assert response['message'] == 'ok'
def test_send_event(mock_requests_post): fauxapi = PfsenseFauxapi(host=None, apikey=None, apisecret=None) mock_requests_post.return_value.status_code = 200 mock_requests_post.return_value.text = '{"callid": "5c8dc64e73efc", "action": "send_event", "message": "ok"}' response = fauxapi.send_event(None) assert response['action'] == 'send_event' assert response['message'] == 'ok'
def test_config_reload(mock_requests_get): fauxapi = PfsenseFauxapi(host=None, apikey=None, apisecret=None) mock_requests_get.return_value.status_code = 200 mock_requests_get.return_value.text = '{"callid": "5c8dbe9a6331e", "action": "config_reload", "message": "ok"}' response = fauxapi.config_reload() assert response['action'] == 'config_reload' assert response['message'] == 'ok'
def __init__(self, config_file): """Program to send pfSense information collected from FauxAPI to InfluxDB""" self.config = configparser.ConfigParser() self.config.read(config_file) self.fauxapi = PfsenseFauxapi(self.config['FAUXAPI']['APIHost'], self.config['FAUXAPI']['APIKey'], self.config['FAUXAPI']['APISecret']) self.influx_client = InfluxDBClient(host=self.config['INFLUXDB']['InfluxDBHost'], port=self.config['INFLUXDB']['Port'], username=self.config['INFLUXDB']['Username'], password=self.config['INFLUXDB']['Password'], database=self.config['INFLUXDB']['Database']) self.pfSense_config = self.fauxapi.config_get() self.pfSense_host = self.pfSense_config['system']['hostname'] + "." + self.pfSense_config['system']['domain']
def test_alias_update_urltables(mock_requests_get): fauxapi = PfsenseFauxapi(host=None, apikey=None, apisecret=None) mock_requests_get.return_value.status_code = 200 mock_requests_get.return_value.text = '{"callid": "5c8dc7720e65a", "action": "alias_update_urltables", "message": "ok", "data": {"updates": []}}' response = fauxapi.alias_update_urltables() assert response['action'] == 'alias_update_urltables' assert response['message'] == 'ok' assert response['data'] is not None
def test_rule_get(mock_requests_get): fauxapi = PfsenseFauxapi(host=None, apikey=None, apisecret=None) mock_requests_get.return_value.status_code = 200 mock_requests_get.return_value.text = '{"callid": "5c8dc731b54e2", "action": "rule_get", "message": "ok", "data": {"rules": []}}' response = fauxapi.rule_get() assert response['action'] == 'rule_get' assert response['message'] == 'ok' assert response['data'] is not None
def test_gateway_status(mock_requests_get): fauxapi = PfsenseFauxapi(host=None, apikey=None, apisecret=None) mock_requests_get.return_value.status_code = 200 mock_requests_get.return_value.text = '{"callid": "5c8dc6227ec06", "action": "gateway_status", "message": "ok", "data": {"gateway_status": {}}}' response = fauxapi.gateway_status() assert response['action'] == 'gateway_status' assert response['message'] == 'ok' assert response['data'] is not None
def test_interface_stats(mock_requests_get): fauxapi = PfsenseFauxapi(host=None, apikey=None, apisecret=None) mock_requests_get.return_value.status_code = 200 mock_requests_get.return_value.text = '{"callid": "5c8dc5d40c07a", "action": "interface_stats", "message": "ok", "data": {"stats": {}}}' response = fauxapi.interface_stats(None) assert response['action'] == 'interface_stats' assert response['message'] == 'ok' assert response['data'] is not None
def test_config_patch(mock_requests_post): fauxapi = PfsenseFauxapi(host=None, apikey=None, apisecret=None) mock_requests_post.return_value.status_code = 200 mock_requests_post.return_value.text = '{"callid": "5c8dbe47090f8", "action": "config_patch", "message": "ok", "data": {"do_backup": true, "do_reload": true, "previous_config_file": "/cf/conf/backup/config-1552793159.xml"}}' response = fauxapi.config_patch(config={}) assert response['action'] == 'config_patch' assert response['message'] == 'ok' assert response['data'] is not None
def test_system_stats(mock_requests_get): fauxapi = PfsenseFauxapi(host=None, apikey=None, apisecret=None) mock_requests_get.return_value.status_code = 200 mock_requests_get.return_value.text = '{"callid": "5c8dc51dabd44", "action": "system_stats", "message": "ok", "data": {"stats": {}}}' response = fauxapi.system_stats() assert response['action'] == 'system_stats' assert response['message'] == 'ok' assert response['data'] is not None
def test_system_info(mock_requests_get): fauxapi = PfsenseFauxapi(host=None, apikey=None, apisecret=None) mock_requests_get.return_value.status_code = 200 mock_requests_get.return_value.text = '{"callid": "5ed39b838c8f6", "action": "system_info", "message": "ok", "data": {"info": "foobar"}}' response = fauxapi.system_info() assert response['action'] == 'system_info' assert response['message'] == 'ok' assert response['data'] is not None
def test_config_backup_list(mock_requests_get): fauxapi = PfsenseFauxapi(host=None, apikey=None, apisecret=None) mock_requests_get.return_value.status_code = 200 mock_requests_get.return_value.text = '{"callid": "5c8dc4987c3f8", "action": "config_backup_list", "message": "ok", "data": {"backup_files": []}}' response = fauxapi.config_backup_list() assert response['action'] == 'config_backup_list' assert response['message'] == 'ok' assert response['data'] is not None
def test_config_backup(mock_requests_get): fauxapi = PfsenseFauxapi(host=None, apikey=None, apisecret=None) mock_requests_get.return_value.status_code = 200 mock_requests_get.return_value.text = '{"callid": "5c8dc46901d7f", "action": "config_backup", "message": "ok", "data": {"backup_config_file": "/cf/conf/backup/config-1552794729.xml"}}' response = fauxapi.config_backup() assert response['action'] == 'config_backup' assert response['message'] == 'ok' assert response['data'] is not None
def __init__(self): self.host = getenv("HOST_ADDRESS") self.port = 443 self.key = getenv("FAUXAPI_KEY") self.secret = getenv("FAUXAPI_SECRET") try: PfsenseFauxapi(f"{self.host}:{self.port}", self.key, self.secret) except PfsenseFauxapiException as e: print(f"error: {str(e)}") else: self.pfapi = PfsenseFauxapi(f"{self.host}:{self.port}", self.key, self.secret)
def main(): print('Starting PfSense Parental Control App') print('Finding LAN devices') devices = find_devices() print('Finding Rule Overides') overrides = get_overrides() print('Finding blocked users') blocked = find_blocked(devices, overrides) print('Resolving IP addresses') blocked_ip = [[build_descr(b), devices.get(b['MAC'])] for b in blocked] print("Configuring Faux API") api = PfsenseFauxapi(PFSENSE_HOST, apikey, apisecret) api.proto = 'http' print("Requesting config") current_block_rules, default_rules = get_rules(api) current_block_ip = dict( (r['descr'], r['source']['address']) for r in current_block_rules) new_rules = build_new_rules(blocked_ip, current_block_ip) new_patch = create_filter_rule_patch(new_rules + default_rules) pprint.pprint(new_patch) print("patching") api.config_patch(new_patch) api.function_call({ 'function': 'filter_configure_sync', 'args': [False], 'includes': ['shaper.inc'], })
def build_faux_api_connection(): # First some URL formatting that I worked on to allow for custom port numbers in your # pfsense server's web configurator URL host_raw = grab_fields_from_file(".pfsense_ip_and_port", "ip") fauxapi_host = "{}:{}".format(host_raw[0], host_raw[1].strip()) # Grab the key and secret from the user created file key_secret = grab_fields_from_file(".pfsense_key_and_secret", "key") # Return callable function to interact with pfsense server try: return PfsenseFauxapi(fauxapi_host, key_secret[0], key_secret[1].strip(), debug=False) except: raise Exception( "Unable to read data files to build connection to the FauxAPI instance or data found was incorrect." )
def set_rule_state(self, action): """Setup the pfSense Rules platform.""" import pprint, sys from PfsenseFauxapi.PfsenseFauxapi import PfsenseFauxapi _LOGGER.debug("Connecting to pfSense firewall to change rule states.") try: # Setup connection with devices/cloud FauxapiLib = PfsenseFauxapi(self._host, self._api_key, self._access_token, debug=True) # Get the current set of filters filters = FauxapiLib.config_get('filter') except: _LOGGER.error( "Problem retrieving rule set from pfSense host: %s. Likely due to API key or secret.", self._host) i = 0 for rule in filters['rule']: if (rule.get('tracker') == self._tracker_id): _LOGGER.info("Found rule changing state rule: %s", self._rule_name) if (action == True): if ('disabled' in rule): del filters['rule'][i]['disabled'] _LOGGER.debug( "Rule %s enabled in config (this has not been pushed back to firewall yet!)", self._rule_name) elif (action == False): filters['rule'][i]['disabled'] = "" _LOGGER.debug( "Rule %s disabled in config (this has not been pushed back to firewall yet!)", self._rule_name) i = i + 1 try: _LOGGER.debug("Sending updated rule set to pfSense firewall") # Push the config back to pfSense filters = FauxapiLib.config_set(filters, 'filter') _LOGGER.debug( "Reloading the config on pfSense firewall to accept rule changes" ) # Reload the config FauxapiLib.send_event("filter reload") except: _LOGGER.error( "Problem sending & reloading rule set from pfSense host: %s. Likely due to API key or secret.", self._host)
class UpdateAwsAliasesFauxapi(): fauxapi_host = None fauxapi_apikey = None fauxapi_apisecret = None system_config = None aws_ipranges_uri = 'https://ip-ranges.amazonaws.com/ip-ranges.json' FauxapiLib = None def __init__(self, fauxapi_host, fauxapi_apikey, fauxapi_apisecret, debug=False): self.FauxapiLib = PfsenseFauxapi(fauxapi_host, fauxapi_apikey, fauxapi_apisecret, debug) def update(self, regions_match='*', services_match='*', ipv4=True, ipv6=True): # Use FauxapiLib to load the remote system config into memory self.system_config = self.FauxapiLib.config_get() # download ip-ranges.json parse and iterate for name, data in sorted(self.get_aws_ipranges().items()): if regions_match == '*' or regions_match.replace('*', '').replace( '-', '').lower() in name: if services_match == '*' or services_match.replace( '*', '').replace('_', '').lower() in name: addresses = [] if ipv4 is True and len(data['ipv4']) > 0: addresses += data['ipv4'] if ipv6 is True and len(data['ipv6']) > 0: addresses += data['ipv6'] self.update_alias_in_config( name=name, description=data['description'], addresses=addresses, aws_create_date=data['aws_create_date']) # Use FauxapiLib to save to the remote system the new edited config result = self.FauxapiLib.config_set(self.system_config) print(json.dumps(result)) def update_alias_in_config(self, name, description, addresses, aws_create_date): addresses.sort() # candidate alias to apply alias_data = { 'name': name, 'type': 'network', 'address': ' '.join(addresses), 'descr': description, 'detail': '||'.join( ['ip-ranges.json createDate: {}'.format(aws_create_date)] * len(addresses)) } if 'aliases' not in self.system_config or type( self.system_config['aliases']) is not dict: self.system_config['aliases'] = {} if 'alias' not in self.system_config['aliases'] or type( self.system_config['aliases']['alias']) is not list: self.system_config['aliases']['alias'] = [] alias_found = False for index, alias in enumerate(self.system_config['aliases']['alias']): if alias['name'] == name: alias_found = True if alias['address'] != alias_data['address']: self.system_config['aliases']['alias'][index] = alias_data if alias_found is False: self.system_config['aliases']['alias'].append(alias_data) def get_aws_ipranges(self): with urllib.request.urlopen(self.aws_ipranges_uri) as response: aws_ipranges_data = json.loads(response.read()) ipranges = {} for prefix in aws_ipranges_data['prefixes'] + aws_ipranges_data[ 'ipv6_prefixes']: name = 'aws_{}_{}'.format( prefix['region'].replace('-', '').lower(), prefix['service'].replace('_', '').lower())[:32] if name not in ipranges: ipranges[name] = { 'ipv4': [], 'ipv6': [], 'description': 'AWS {region} {service}'.format(region=prefix['region'], service=prefix['service']), 'aws_create_date': aws_ipranges_data['createDate'] } if 'ip_prefix' in prefix and len(prefix['ip_prefix']) > 0: ipranges[name]['ipv4'].append(prefix['ip_prefix']) if 'ipv6_prefix' in prefix and len(prefix['ipv6_prefix']) > 0: ipranges[name]['ipv6'].append(prefix['ipv6_prefix']) return ipranges
class PfsenseCollector(BaseCollector): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._fapi = PfsenseFauxapi( self.config['api_host'], self.config['key'], self.config['secret'], ) def get_data_for_sub(self) -> List[Gdata]: ret = [] try: stats = self._get_stats() except Exception as e: logging.error( f'Failed to get stats from {self.config["api_host"]}: {e}') return ret ret.extend(self._get_int_gdata(stats)) ret.append(self._get_sys_gdata(stats)) return ret def _get_int_gdata(self, stats) -> List[Gdata]: """ Turn the interface stats into Gdata objects """ ret = [] for iface, data in stats['interface'].items(): dsnames = [] values = [] dstypes = [] for k, v in data.items(): dsnames.append(k) if k == 'mtu': dstypes.append('gauge') else: dstypes.append('counter') values.append(float(v)) ret.append( Gdata( plugin='pfsense', dtype='interface', dtype_instance=iface, dsnames=dsnames, dstypes=dstypes, values=values, interval=float(self.config['interval']), host=self.config['gdata_host'], )) return ret def _get_sys_gdata(self, stats) -> Gdata: return Gdata( plugin='pfsense', dtype='system', dsnames=list(stats['system'].keys()), dstypes=['gauge'] * len(stats['system']), values=list(stats['system'].values()), interval=float(self.config['interval']), host=self.config['gdata_host'], ) def _get_stats(self): """ Get stats from the server """ ret = {'interface': {}} # First, get the interface stats for iface in self.config['interfaces'].split(): iface = iface.strip() stats = self._fapi.interface_stats(iface) if stats and stats['message'] == 'ok': ret['interface'][iface] = stats['data']['stats'] else: logging.warning(f'Failed to get interface stats for {iface}') # Now get the system stats stats = self._fapi.system_stats() if stats and stats['message'] == 'ok': ret['system'] = { 'load_avg_1': float(stats['data']['stats']['load_average'][0]), 'load_avg_5': float(stats['data']['stats']['load_average'][1]), 'load_avg_15': float(stats['data']['stats']['load_average'][2]), 'pfstateperc': float(stats['data']['stats']['pfstatepercent']), 'mbufperc': float(stats['data']['stats']['mbufpercent']), 'temp': float(stats['data']['stats']['temp']), } return ret
print(' $ ' + sys.argv[0] + ' <host> | jq .') print() sys.exit(1) # check args and env exist if(len(sys.argv) != 2) or not os.getenv('FAUXAPI_APIKEY') or not os.getenv('FAUXAPI_APISECRET'): usage() # config fauxapi_host=sys.argv[1] fauxapi_apikey=os.getenv('FAUXAPI_APIKEY') fauxapi_apisecret=os.getenv('FAUXAPI_APISECRET') FauxapiLib = PfsenseFauxapi(fauxapi_host, fauxapi_apikey, fauxapi_apisecret, debug=False) # config get the full configuration and simply print to console # ============================================================================= config = FauxapiLib.config_get() print(json.dumps( config )) # config set the full configuration # ============================================================================= # NB: nothing amazing is happening here, we are simply writing back the same (full) configuration again, the developer # most likely wants to make changes to `config` before calling the config_set function again here print(json.dumps( FauxapiLib.config_set(config)
class PfSense: """ PfSense Thingy... ... Methods ------- get_openvpn_settings(self) -> dict: set_pfsense_config(self, data: dict, vpnid: list, refresh: bool = None) -> dict: get_pf_openvpn_clients(self) -> dict: get_pf_openvpn_locations(self, vpn_clients: dict) -> set: """ def __init__(self): self.host = getenv("HOST_ADDRESS") self.port = 443 self.key = getenv("FAUXAPI_KEY") self.secret = getenv("FAUXAPI_SECRET") try: PfsenseFauxapi(f"{self.host}:{self.port}", self.key, self.secret) except PfsenseFauxapiException as e: print(f"error: {str(e)}") else: self.pfapi = PfsenseFauxapi(f"{self.host}:{self.port}", self.key, self.secret) def get_openvpn_settings(self) -> dict: try: pf_openvpn_settings = self.pfapi.config_get('openvpn') except PfsenseFauxapiException as e: return {"error": str(e)} else: return pf_openvpn_settings def set_pfsense_config(self, data: dict, vpnid: list, refresh: bool = None) -> dict: """ Parameters ---------- data : dict . vpnid : list . refresh : bool . """ try: resp = self.pfapi.config_set(data, 'openvpn') except PfsenseFauxapiException as e: return {"error": str(e)} else: if refresh: self.pfapi.config_reload() if len(vpnid): for vid in vpnid: data = self.pfapi.function_call({ "function": "openvpn_restart_by_vpnid", "args": ["client", f"{vid}"] }) return resp def get_pf_openvpn_clients(self) -> dict: clients: list = [] vpn_clients = self.get_openvpn_settings() if "error" in vpn_clients.keys(): return vpn_clients locations: set = self.get_pf_openvpn_locations(vpn_clients) for vpnclient in vpn_clients["openvpn-client"]: clients.append(vpnclient["server_addr"]) return {"clients": clients, "locations": list(locations)} def get_pf_openvpn_locations(self, vpn_clients: dict) -> set: """ Parameters ---------- vpn_clients : dict . """ locations = set() for client in vpn_clients["openvpn-client"]: loc = is_vpn_address(client["server_addr"]) if loc is not None: locations.add(loc[1].lower()) return locations
class UserGroupManagementFauxapi(): fauxapi_host = None fauxapi_apikey = None fauxapi_apisecret = None system_config = None FauxapiLib = None def __init__(self, fauxapi_host, fauxapi_apikey, fauxapi_apisecret, debug=False): self.FauxapiLib = PfsenseFauxapi(fauxapi_host, fauxapi_apikey, fauxapi_apisecret, debug) # user functions # ========================================================================= def get_users(self): self._reload_system_config() response_data = {} for user in self.system_config['system']['user']: response_data[user['name']] = user del (response_data[user['name']]['name']) return response_data def add_user(self, username): self._reload_system_config() user_index, user = self._get_entity('user', username) if user_index is not None: raise UserGroupManagementFauxapiException('user already exists', username) user = { 'scope': 'user', 'bcrypt-hash': 'no-password-set', 'descr': '', 'name': username, 'expires': '', 'dashboardcolumns': '2', 'authorizedkeys': '', 'ipsecpsk': '', 'webguicss': 'pfSense.css', 'uid': self._get_next_id('uid'), } patch_system_user = { 'system': { 'user': self.system_config['system']['user'] } } patch_system_user['system']['user'].append(user) response = self.FauxapiLib.config_patch(patch_system_user) if response['message'] != 'ok': raise UserGroupManagementFauxapiException('unable to add user', response['message']) self._increment_next_id('uid') return user def manage_user(self, username, attributes): self._reload_system_config() valid_attributes = [ 'password', 'descr', 'expires', 'dashboardcolumns', 'authorizedkeys', 'ipsecpsk', 'webguicss', 'disabled', 'priv' ] user_index, user = self._get_entity('user', username) if user_index is None: raise UserGroupManagementFauxapiException('user does not exist', username) if type(attributes) != dict: raise UserGroupManagementFauxapiException( 'attributes is incorrect type') for attribute, value in attributes.items(): if attribute not in valid_attributes: raise UserGroupManagementFauxapiException( 'unsupported attribute type', attribute) if attribute == 'disabled': if value is True: user[attribute] = '' else: if attribute in user: del (user[attribute]) elif attribute == 'password': user['bcrypt-hash'] = bcrypt.hashpw( value.encode('utf8'), bcrypt.gensalt()).decode('utf8') else: if len(value) == 0 and attribute in user: del (user[attribute]) elif len(value) > 0: user[attribute] = value patch_system_user = { 'system': { 'user': self.system_config['system']['user'] } } patch_system_user['system']['user'][user_index] = user response = self.FauxapiLib.config_patch(patch_system_user) if response['message'] != 'ok': raise UserGroupManagementFauxapiException('unable to manage user', response['message']) return user def remove_user(self, username): self._reload_system_config() user_index, user = self._get_entity('user', username) if user_index is None: raise UserGroupManagementFauxapiException('user does not exist', username) patch_system_user = { 'system': { 'user': self.system_config['system']['user'] } } del (patch_system_user['system']['user'][user_index]) response = self.FauxapiLib.config_patch(patch_system_user) if response['message'] != 'ok': raise UserGroupManagementFauxapiException('unable to remove user', response['message']) return user # group functions # ========================================================================= def get_groups(self): self._reload_system_config() response_data = {} for group in self.system_config['system']['group']: response_data[group['name']] = group del (response_data[group['name']]['name']) return response_data def add_group(self, groupname): self._reload_system_config() group_index, group = self._get_entity('group', groupname) if group_index is not None: raise UserGroupManagementFauxapiException('group already exists', groupname) group = { 'scope': 'local', 'description': '', 'name': groupname, 'gid': self._get_next_id('gid'), } patch_system_group = { 'system': { 'group': self.system_config['system']['group'] } } patch_system_group['system']['group'].append(group) response = self.FauxapiLib.config_patch(patch_system_group) if response['message'] != 'ok': raise UserGroupManagementFauxapiException('unable to add group', response['message']) self._increment_next_id('gid') return group def manage_group(self, groupname, attributes): self._reload_system_config() valid_attributes = ['description', 'member', 'priv'] group_index, group = self._get_entity('group', groupname) if group_index is None: raise UserGroupManagementFauxapiException('group does not exist', groupname) if type(attributes) != dict: raise UserGroupManagementFauxapiException( 'attributes is incorrect type') for attribute, value in attributes.items(): if attribute not in valid_attributes: raise UserGroupManagementFauxapiException( 'unsupported attribute type', attribute) if attribute == 'member': if type(value) != list: raise UserGroupManagementFauxapiException( 'member attribute is incorrect type') elif attribute == 'priv': if type(value) != list: raise UserGroupManagementFauxapiException( 'priv attribute is incorrect type') if len(value) == 0 and attribute in group: del (group[attribute]) elif len(value) > 0: group[attribute] = value patch_system_group = { 'system': { 'group': self.system_config['system']['group'] } } patch_system_group['system']['group'][group_index] = group response = self.FauxapiLib.config_patch(patch_system_group) if response['message'] != 'ok': raise UserGroupManagementFauxapiException('unable to manage group', response['message']) return group def remove_group(self, groupname): self._reload_system_config() group_index, group = self._get_entity('group', groupname) if group_index is None: raise UserGroupManagementFauxapiException('group does not exist', groupname) patch_system_group = { 'system': { 'group': self.system_config['system']['group'] } } del (patch_system_group['system']['group'][group_index]) response = self.FauxapiLib.config_patch(patch_system_group) if response['message'] != 'ok': raise UserGroupManagementFauxapiException('unable to remove group', response['message']) return group # internal helper functions # ========================================================================= def _get_entity(self, entity_type, entity_name): entity = None entity_index = 0 for entity_data in self.system_config['system'][entity_type]: if entity_data['name'] == entity_name: entity = entity_data break entity_index += 1 if entity is None: return None, None return entity_index, entity def _get_next_id(self, id_type): id_name = 'next{}'.format(id_type) return self.system_config['system'][id_name] def _increment_next_id(self, id_type): id_name = 'next{}'.format(id_type) next_id = int(self._get_next_id(id_type)) + 1 patch_system_nextid = {'system': {id_name: str(next_id)}} response = self.FauxapiLib.config_patch(patch_system_nextid) if response['message'] != 'ok': raise UserGroupManagementFauxapiException( 'unable to increment the nextid', id_type) return next_id def _reload_system_config(self): self.system_config = self.FauxapiLib.config_get()
def test_version_exist(): fauxapi = PfsenseFauxapi(host=None, apikey=None, apisecret=None) assert fauxapi.version is not None
class pfSenseInfluxCollector: def __init__(self, config_file): """Program to send pfSense information collected from FauxAPI to InfluxDB""" self.config = configparser.ConfigParser() self.config.read(config_file) self.fauxapi = PfsenseFauxapi(self.config['FAUXAPI']['APIHost'], self.config['FAUXAPI']['APIKey'], self.config['FAUXAPI']['APISecret']) self.influx_client = InfluxDBClient(host=self.config['INFLUXDB']['InfluxDBHost'], port=self.config['INFLUXDB']['Port'], username=self.config['INFLUXDB']['Username'], password=self.config['INFLUXDB']['Password'], database=self.config['INFLUXDB']['Database']) self.pfSense_config = self.fauxapi.config_get() self.pfSense_host = self.pfSense_config['system']['hostname'] + "." + self.pfSense_config['system']['domain'] def run(self): """Run enabled modules wuth delay as defined in config file""" threading.Timer(int(self.config['GENERAL']['Delay']), self.run).start() for function in self.module_dictionary: if self.config['MODULES'].getboolean(function): self.module_dictionary[function](self) def fauxapi_function_call(self, functionName): """Call fauxapi function_call without function arguments""" data = { "function": functionName } return self.fauxapi.function_call(data)['data']['return'] def fauxapi_function_call_args(self, functionName, args): """Call fauxapi function_call function with arguments""" data = { "function": functionName, "args": args } return self.fauxapi.function_call(data)['data']['return'] def write_influxdb_data(self, data): """Write set of data to InfluxDB.""" self.influx_client.write_points(data) def gateway_status(self): """Get and save information about statuses of gateways.""" gateways = self.fauxapi_function_call_args("return_gateways_status", "true") for gateway in gateways: gateway_status = 0 if gateways[gateway]['status'] == "down" else 1 gateway_data = [ { "measurement": "gateway_status", "fields": { "name": gateways[gateway]['name'], "rtt": gateways[gateway]['delay'], "rttsd": gateways[gateway]['stddev'], "status": gateway_status }, "tags": { "host": self.pfSense_host } } ] self.write_influxdb_data(gateway_data) def interface_statistics(self): """Get and save information about interfaces statistics.""" interface_descriptions = self.fauxapi_function_call("get_configured_interface_with_descr") for interface in interface_descriptions: interface_info = self.fauxapi_function_call_args("get_interface_info", interface) interface_data = [ { "measurement": "interface_statistics", "fields": { "bytes_in": interface_info['inbytes'], "bytes_out": interface_info['outbytes'], "collisions": interface_info['collisions'], "errors_in": interface_info['inerrs'], "errors_out": interface_info['outerrs'], "name": interface_descriptions[interface], "packets_in": interface_info['inpkts'], "packets_out": interface_info['outpkts'] }, "tags": { "host": self.pfSense_host, "interface": interface_info['if'] } } ] self.write_influxdb_data(interface_data) def interface_status(self): """Get and save information about statuses of interfaces.""" interface_descriptions = self.fauxapi_function_call("get_configured_interface_with_descr") for interface in interface_descriptions: interface_info = self.fauxapi_function_call_args("get_interface_info", interface) interface_data = [ { "measurement": "interface_status", "fields": { "ip_address": interface_info['ipaddr'], "name": interface_descriptions[interface], "status": 1 if interface_info['status'] == 'up' else 0 }, "tags": { "host": self.pfSense_host, "interface": interface_info['if'] } } ] self.write_influxdb_data(interface_data) def openvpn_client_status(self): """Get and save information about OpenVPN clients.""" for client in self.fauxapi_function_call("openvpn_get_active_clients"): client_data = [ { "measurement": "openvpn_client_status", "fields": { "remote_host": client['remote_host'], "status": 1 if client['status'] == "up" else 0, "virtual_address": client['virtual_addr'] }, "tags": { "host": self.pfSense_host, "vpnid": int(client['vpnid']), } } ] self.write_influxdb_data(client_data) def openvpn_connected_clients(self): """Get and save information about connected clients to OpenVPN server.""" for server in self.fauxapi_function_call("openvpn_get_active_servers"): server_name = server['name'] for connection in server['conns']: connection_data = [ { "measurement": "openvpn_connected_clients", "fields": { "client_id": int(connection['client_id']), "common_name": connection['common_name'], "remote_host": connection['remote_host'], "virtual_address": connection['virtual_addr'] }, "tags": { "host": self.pfSense_host, "server": server_name } } ] self.write_influxdb_data(connection_data) connected_clients_data = [ { "measurement": "openvpn_connected_clients", "fields": { "connected_clients": len(server['conns']) }, "tags": { "host": self.pfSense_host, "server": server_name } } ] self.write_influxdb_data(connected_clients_data) def services_status(self): """Get and save information about statuses of services.""" for service in self.fauxapi_function_call("get_services")[1:]: service_status = self.fauxapi_function_call_args("get_service_status", [service]) service_data = [ { "measurement": "services_status", "fields": { "description": service['description'], "status": 1 if service_status else 0 }, "tags": { "host": self.pfSense_host, "service": service['name'] } } ] self.write_influxdb_data(service_data) # Module Dictionary to allow dynamic calling of functions depending on config file module_dictionary = {"Gateway_Status": gateway_status, "Interface_Statistics": interface_statistics, "Interface_Status": interface_status, "OpenVPN_Client_Status": openvpn_client_status, "OpenVPN_Connected_Clients": openvpn_connected_clients, "Services_Status": services_status}