def __init__(self, module, pfsense=None): if pfsense is None: pfsense = PFSenseModule(module) self.module = module # ansible module self.name = None # ansible module name self.params = None # ansible input parameters self.pfsense = pfsense # helper module self.apply = True # apply configuration at the end self.obj = None # dict holding target pfsense parameters self.target_elt = None # xml object holding target pfsense parameters self.root_elt = None # xml parent of target_elt self.change_descr = '' self.result = {} self.result['changed'] = False self.result['commands'] = [] self.diff = {'after': {}, 'before': {}} self.result['diff'] = self.diff
class PFSenseModuleIpsecAggregate(object): """ module managing pfsense aggregated ipsec tunnels, phases 1, phases 2 and proposals """ def __init__(self, module): self.module = module self.pfsense = PFSenseModule(module) self.pfsense_ipsec = PFSenseIpsecModule(module, self.pfsense) self.pfsense_ipsec_proposal = PFSenseIpsecProposalModule( module, self.pfsense) self.pfsense_ipsec_p2 = PFSenseIpsecP2Module(module, self.pfsense) def _update(self): if self.pfsense_ipsec.result[ 'changed'] or self.pfsense_ipsec_proposal.result[ 'changed'] or self.pfsense_ipsec_p2.result['changed']: return self.pfsense.phpshell( "require_once('vpn.inc');" "$ipsec_dynamic_hosts = vpn_ipsec_configure();" "$retval = 0;" "$retval |= filter_configure();" "if ($ipsec_dynamic_hosts >= 0 && is_subsystem_dirty('ipsec'))" " clear_subsystem_dirty('ipsec');") return ('', '', '') @staticmethod def want_ipsec(ipsec_elt, ipsecs): """ return True if we want to keep ipsec_elt """ descr = ipsec_elt.find('descr') if descr is None: return True for ipsec in ipsecs: if ipsec['state'] == 'absent': continue if ipsec['descr'] == descr.text: return True return False def proposal_elt_to_params(self, ipsec_elt, proposal_elt): """ return the pfsense_ipsec_proposal params corresponding the proposal_elt """ params = {} proposal = self.pfsense.element_to_dict(proposal_elt) params['encryption'] = proposal['encryption-algorithm']['name'] params['key_length'] = proposal['encryption-algorithm'].get('keylen') if params['key_length'] is not None: if params['key_length'] == '': params['key_length'] = None else: params['key_length'] = int(params['key_length']) params['hash'] = proposal['hash-algorithm'] params['dhgroup'] = int(proposal['dhgroup']) descr_elt = ipsec_elt.find('descr') if descr_elt is None: params['descr'] = '' else: params['descr'] = descr_elt.text return params def want_ipsec_proposal(self, ipsec_elt, proposal_elt, proposals): """ return True if we want to keep proposal_elt """ params_from_elt = self.proposal_elt_to_params(ipsec_elt, proposal_elt) params_from_elt['state'] = 'present' if proposals is not None: for proposal in proposals: _proposal = deepcopy(proposal) _proposal.pop('apply', None) if params_from_elt == _proposal: return True return False def want_ipsec_phase2(self, phase2_elt, phases2): """ return True if we want to keep proposal_elt """ ikeid_elt = phase2_elt.find('ikeid') descr = phase2_elt.find('descr') if descr is None or ikeid_elt is None: return True phase1_elt = self.pfsense.find_ipsec_phase1(ikeid_elt.text, 'ikeid') if phase1_elt is None: return True phase1_descr_elt = phase1_elt.find('descr') if phase1_descr_elt is None: return True p1_descr = phase1_descr_elt.text if phases2 is not None: for phase2 in phases2: if phase2['state'] == 'absent': continue if phase2['descr'] == descr.text and phase2[ 'p1_descr'] == p1_descr: return True return False def run_ipsecs(self): """ process input params to add/update/delete all ipsecs tunnels """ want = self.module.params['aggregated_ipsecs'] # processing aggregated parameter if want is not None: for param in want: self.pfsense_ipsec.run(param) # delete every other if required if self.module.params['purge_ipsecs']: todel = [] for ipsec_elt in self.pfsense_ipsec.root_elt: if ipsec_elt.tag != 'phase1': continue if not self.want_ipsec(ipsec_elt, want): params = {} params['state'] = 'absent' params['apply'] = False params['descr'] = ipsec_elt.find('descr').text params['ikeid'] = ipsec_elt.find('ikeid').text todel.append(params) for params in todel: self.pfsense_ipsec.run(params) def run_ipsec_proposals(self): """ process input params to add/update/delete all ipsecs tunnels """ want = self.module.params['aggregated_ipsec_proposals'] # processing aggregated parameter if want is not None: for param in want: self.pfsense_ipsec_proposal.run(param) # delete every other if required if self.module.params['purge_ipsec_proposals']: todel = [] for ipsec_elt in self.pfsense_ipsec_proposal.ipsec: if ipsec_elt.tag != 'phase1': continue encryption_elt = ipsec_elt.find('encryption') if encryption_elt is None: continue items_elt = encryption_elt.findall('item') for proposal_elt in items_elt: if not self.want_ipsec_proposal(ipsec_elt, proposal_elt, want): params = self.proposal_elt_to_params( ipsec_elt, proposal_elt) params['state'] = 'absent' params['apply'] = False params['descr'] = ipsec_elt.find('descr').text params['ikeid'] = ipsec_elt.find('ikeid').text todel.append(params) for params in todel: self.pfsense_ipsec_proposal.run(params) def run_ipsec_p2s(self): """ process input params to add/update/delete all ipsecs tunnels """ want = self.module.params['aggregated_ipsec_p2s'] # processing aggregated parameter if want is not None: for param in want: self.pfsense_ipsec_p2.run(param) # delete every other if required if self.module.params['purge_ipsec_p2s']: todel = [] for phase2_elt in self.pfsense_ipsec_p2.root_elt: if phase2_elt.tag != 'phase2': continue if not self.want_ipsec_phase2(phase2_elt, want): params = {} params['state'] = 'absent' params['apply'] = False params['descr'] = phase2_elt.find('descr').text params['p1_descr'] = self.pfsense.find_ipsec_phase1( phase2_elt.find('ikeid').text, 'ikeid').find('descr').text params['ikeid'] = phase2_elt.find('ikeid').text todel.append(params) for params in todel: self.pfsense_ipsec_p2.run(params) def commit_changes(self): """ apply changes and exit module """ stdout = '' stderr = '' changed = self.pfsense_ipsec.result[ 'changed'] or self.pfsense_ipsec_proposal.result[ 'changed'] or self.pfsense_ipsec_p2.result['changed'] if changed and not self.module.check_mode: self.pfsense.write_config(descr='aggregated change') if self.module.params['apply']: (dummy, stdout, stderr) = self._update() result = {} result['result_ipsecs'] = self.pfsense_ipsec.result['commands'] result['result_ipsec_proposals'] = self.pfsense_ipsec_proposal.result[ 'commands'] result['result_ipsec_p2s'] = self.pfsense_ipsec_p2.result['commands'] result['changed'] = changed result['stdout'] = stdout result['stderr'] = stderr self.module.exit_json(**result)
def __init__(self, module): self.module = module self.pfsense = PFSenseModule(module) self.system = self.pfsense.get_element('system') self.groups = self.system.findall('group')
class pfSenseGroup(object): def __init__(self, module): self.module = module self.pfsense = PFSenseModule(module) self.system = self.pfsense.get_element('system') self.groups = self.system.findall('group') def _find_group(self, name): found = None i = 0 for group in self.groups: i = list(self.system).index(group) if group.find('name').text == name: found = group break return (found, i) def add(self, group): group_elt, i = self._find_group(group['name']) changed = False if group_elt is None: changed = True if self.module.check_mode: self.module.exit_json(changed=True) group_elt = self.pfsense.new_element('group') self.pfsense.copy_dict_to_element(group, group_elt) self.system.insert(i + 1, group_elt) self.pfsense.write_config( descr='ansible pfsensible.core.group added %s' % (group['name'])) else: changed = self.pfsense.copy_dict_to_element(group, group_elt) if self.module.check_mode: self.module.exit_json(changed=changed) if changed: self.pfsense.write_config( descr='ansible pfsensible.core.group updated "%s"' % (group['name'])) self.module.exit_json(changed=changed) def remove(self, group): group_elt, dummy = self._find_group(group['name']) changed = False if group_elt is not None: if self.module.check_mode: self.module.exit_json(changed=True) self.system.remove(group_elt) changed = True self.pfsense.write_config( descr='ansible pfsensible.core.group removed "%s"' % (group['name'])) self.module.exit_json(changed=changed)
class PFSenseModuleAggregate(object): """ module managing pfsense aggregated aliases, rules, rule separators, interfaces and VLANs """ def __init__(self, module): self.module = module self.pfsense = PFSenseModule(module) self.pfsense_aliases = PFSenseAliasModule(module, self.pfsense) self.pfsense_interfaces = PFSenseInterfaceModule(module, self.pfsense) self.pfsense_nat_outbounds = PFSenseNatOutboundModule( module, self.pfsense) self.pfsense_nat_port_forwards = PFSenseNatPortForwardModule( module, self.pfsense) self.pfsense_rules = PFSenseRuleModule(module, self.pfsense) self.pfsense_rule_separators = PFSenseRuleSeparatorModule( module, self.pfsense) self.pfsense_vlans = PFSenseVlanModule(module, self.pfsense) def _update(self): run = False cmd = 'require_once("filter.inc");\n' # TODO: manage one global list of commands as ordering can be important between modules if self.pfsense_vlans.result['changed']: run = True cmd += self.pfsense_vlans.get_update_cmds() if self.pfsense_interfaces.result['changed']: run = True cmd += self.pfsense_interfaces.get_update_cmds() cmd += 'if (filter_configure() == 0) { \n' if self.pfsense_aliases.result['changed']: run = True cmd += 'clear_subsystem_dirty(\'aliases\');\n' if self.pfsense_nat_port_forwards.result[ 'changed'] or self.pfsense_nat_outbounds.result['changed']: run = True cmd += 'clear_subsystem_dirty(\'natconf\');\n' if (self.pfsense_rules.result['changed'] or self.pfsense_rule_separators.result['changed'] or self.pfsense_nat_port_forwards.result['changed'] or self.pfsense_nat_outbounds.result['changed']): run = True cmd += 'clear_subsystem_dirty(\'filter\');\n' cmd += '}' if run: return self.pfsense.phpshell(cmd) return ('', '', '') def _parse_floating_interfaces(self, interfaces): """ parse interfaces """ res = set() for interface in interfaces.split(','): res.add(self.pfsense.parse_interface(interface)) return res def want_rule(self, rule_elt, rules, name_field='name'): """ return True if we want to keep rule_elt """ descr = rule_elt.find('descr') interface = rule_elt.find('interface') floating = rule_elt.find('floating') is not None # probably not a rule if descr is None or interface is None: return True for rule in rules: if rule['state'] == 'absent': continue if rule[name_field] != descr.text: continue rule_floating = ( rule.get('floating') is not None and (isinstance(rule['floating'], bool) and rule['floating'] or rule['floating'].lower() in ['yes', 'true'])) if floating != rule_floating: continue if floating or self.pfsense.parse_interface( rule['interface']) == interface.text: return True return False def want_rule_separator(self, separator_elt, rule_separators): """ return True if we want to keep separator_elt """ name = separator_elt.find('text').text interface = separator_elt.find('if').text for separator in rule_separators: if separator['state'] == 'absent': continue if separator['name'] != name: continue if separator.get('floating'): if interface == 'floatingrules': return True elif self.pfsense.parse_interface( separator['interface']) == interface: return True return False @staticmethod def want_alias(alias_elt, aliases): """ return True if we want to keep alias_elt """ name = alias_elt.find('name') alias_type = alias_elt.find('type') # probably not an alias if name is None or type is None: return True for alias in aliases: if alias['state'] == 'absent': continue if alias['name'] == name.text and alias['type'] == alias_type.text: return True return False @staticmethod def want_interface(interface_elt, interfaces): """ return True if we want to keep interface_elt """ descr_elt = interface_elt.find('descr') if descr_elt is not None and descr_elt.text: name = descr_elt.text else: name = interface_elt.tag for interface in interfaces: if interface['state'] == 'absent': continue if interface['descr'] == name: return True return False @staticmethod def want_vlan(vlan_elt, vlans): """ return True if we want to keep vlan_elt """ tag = int(vlan_elt.find('tag').text) interface = vlan_elt.find('if') for vlan in vlans: if vlan['state'] == 'absent': continue if vlan['vlan_id'] == tag and vlan['interface'] == interface.text: return True return False @staticmethod def is_filtered(interface_filter, params): if interface_filter is None: return False if 'floating' in params: if isinstance(params['floating'], str): floating = params['floating'].lower() else: floating = 'true' if params['floating'] else 'false' if floating != 'false' and floating != 'no': return 'floating' not in interface_filter return params['interface'].lower() not in interface_filter def run_rules(self): """ process input params to add/update/delete all rules """ want = self.module.params['aggregated_rules'] interface_filter = self.module.params['interface_filter'].lower( ).split(' ') if self.module.params.get( 'interface_filter') is not None else None if want is None: return # delete every other rule if required if self.module.params['purge_rules']: todel = [] for rule_elt in self.pfsense_rules.root_elt: if not self.want_rule(rule_elt, want): params = {} params['state'] = 'absent' params['name'] = rule_elt.find('descr').text if rule_elt.find('floating') is not None: params['floating'] = True interfaces = rule_elt.find('interface').text.split(',') params['interface'] = list() for interface in interfaces: target = self.pfsense.get_interface_display_name( interface, return_none=True) if target is not None: params['interface'].append(target) else: params['interface'].append(interface) params['interface'] = ','.join(params['interface']) else: params[ 'interface'] = self.pfsense.get_interface_display_name( rule_elt.find('interface').text, return_none=True) if params['interface'] is None: continue todel.append(params) for params in todel: if self.is_filtered(interface_filter, params): continue self.pfsense_rules.run(params) # generating order if required if self.module.params.get('order_rules'): last_rules = dict() for params in want: if params.get('before') is not None or params.get( 'after') is not None: self.module.fail_json( msg= "You can't use after or before parameters on rules when using order_rules (see {0})" .format(params['name'])) if params.get('state') == 'absent': continue if params.get('floating'): key = 'floating' else: key = params['interface'] # first rule on interface if key not in last_rules: params['after'] = 'top' last_rules[key] = params['name'] continue params['after'] = last_rules[key] last_rules[key] = params['name'] # processing aggregated parameters for params in want: if self.is_filtered(interface_filter, params): continue self.pfsense_rules.run(params) def run_nat_outbounds_rules(self): """ process input params to add/update/delete all nat_outbound rules """ want = self.module.params['aggregated_nat_outbounds'] interface_filter = self.module.params['interface_filter'].lower( ).split(' ') if self.module.params.get( 'interface_filter') is not None else None if want is None: return # delete every other rule if required if self.module.params['purge_nat_outbounds']: todel = [] for rule_elt in self.pfsense_nat_outbounds.root_elt: if not self.want_rule(rule_elt, want, name_field='descr'): params = {} params['state'] = 'absent' params['descr'] = rule_elt.find('descr').text params[ 'interface'] = self.pfsense.get_interface_display_name( rule_elt.find('interface').text, return_none=True) if params['interface'] is None: continue todel.append(params) for params in todel: if self.is_filtered(interface_filter, params): continue self.pfsense_nat_outbounds.run(params) # processing aggregated parameters for params in want: if self.is_filtered(interface_filter, params): continue self.pfsense_nat_outbounds.run(params) def run_nat_port_forwards_rules(self): """ process input params to add/update/delete all nat_port_forwards_rule rules """ want = self.module.params['aggregated_nat_port_forwards'] interface_filter = self.module.params['interface_filter'].lower( ).split(' ') if self.module.params.get( 'interface_filter') is not None else None if want is None: return # delete every other rule if required if self.module.params['purge_nat_port_forwards']: todel = [] for rule_elt in self.pfsense_nat_port_forwards.root_elt: if not self.want_rule(rule_elt, want, name_field='descr'): params = {} params['state'] = 'absent' params['descr'] = rule_elt.find('descr').text params[ 'interface'] = self.pfsense.get_interface_display_name( rule_elt.find('interface').text, return_none=True) if params['interface'] is None: continue todel.append(params) for params in todel: if self.is_filtered(interface_filter, params): continue self.pfsense_nat_port_forwards.run(params) # processing aggregated parameters for params in want: if self.is_filtered(interface_filter, params): continue self.pfsense_nat_port_forwards.run(params) def run_aliases(self): """ process input params to add/update/delete all aliases """ want = self.module.params['aggregated_aliases'] if want is None: return # processing aggregated parameter for param in want: self.pfsense_aliases.run(param) # delete every other alias if required if self.module.params['purge_aliases']: todel = [] for alias_elt in self.pfsense_aliases.root_elt: if not self.want_alias(alias_elt, want): params = {} params['state'] = 'absent' params['name'] = alias_elt.find('name').text todel.append(params) for params in todel: self.pfsense_aliases.run(params) def run_interfaces(self): """ process input params to add/update/delete all interfaces """ want = self.module.params['aggregated_interfaces'] if want is None: return # processing aggregated parameter for param in want: self.pfsense_interfaces.run(param) # delete every other if required if self.module.params['purge_interfaces']: todel = [] for interface_elt in self.pfsense_interfaces.root_elt: if not self.want_interface(interface_elt, want): params = {} params['state'] = 'absent' descr_elt = interface_elt.find('descr') if descr_elt is not None and descr_elt.text: params['descr'] = descr_elt.text todel.append(params) for params in todel: self.pfsense_interfaces.run(params) def run_rule_separators(self): """ process input params to add/update/delete all separators """ want = self.module.params['aggregated_rule_separators'] interface_filter = self.module.params['interface_filter'].lower( ).split(' ') if self.module.params.get( 'interface_filter') is not None else None if want is None: return # processing aggregated parameter for params in want: if self.is_filtered(interface_filter, params): continue self.pfsense_rule_separators.run(params) # delete every other if required if self.module.params['purge_rule_separators']: todel = [] for interface_elt in self.pfsense_rule_separators.separators: for separator_elt in interface_elt: if not self.want_rule_separator(separator_elt, want): params = {} params['state'] = 'absent' params['name'] = separator_elt.find('text').text if interface_elt.tag == 'floatingrules': params['floating'] = True else: params[ 'interface'] = self.pfsense.get_interface_display_name( interface_elt.tag, return_none=True) if params['interface'] is None: continue todel.append(params) for params in todel: if self.is_filtered(interface_filter, params): continue self.pfsense_rule_separators.run(params) def run_vlans(self): """ process input params to add/update/delete all VLANs """ want = self.module.params['aggregated_vlans'] if want is None: return # processing aggregated parameter for param in want: self.pfsense_vlans.run(param) # delete every other if required if self.module.params['purge_vlans']: todel = [] for vlan_elt in self.pfsense_vlans.root_elt: if not self.want_vlan(vlan_elt, want): params = {} params['state'] = 'absent' params['interface'] = vlan_elt.find('if').text params['vlan_id'] = int(vlan_elt.find('tag').text) todel.append(params) for params in todel: self.pfsense_vlans.run(params) def commit_changes(self): """ apply changes and exit module """ stdout = '' stderr = '' changed = (self.pfsense_aliases.result['changed'] or self.pfsense_interfaces.result['changed'] or self.pfsense_nat_outbounds.result['changed'] or self.pfsense_nat_port_forwards.result['changed'] or self.pfsense_rules.result['changed'] or self.pfsense_rule_separators.result['changed'] or self.pfsense_vlans.result['changed']) if changed and not self.module.check_mode: self.pfsense.write_config(descr='aggregated change') (dummy, stdout, stderr) = self._update() result = {} result['result_aliases'] = self.pfsense_aliases.result['commands'] result['result_interfaces'] = self.pfsense_interfaces.result[ 'commands'] result['result_nat_outbounds'] = self.pfsense_nat_outbounds.result[ 'commands'] result[ 'result_nat_port_forwards'] = self.pfsense_nat_port_forwards.result[ 'commands'] result['result_rules'] = self.pfsense_rules.result['commands'] result['result_rule_separators'] = self.pfsense_rule_separators.result[ 'commands'] result['result_vlans'] = self.pfsense_vlans.result['commands'] result['changed'] = changed result['stdout'] = stdout result['stderr'] = stderr self.module.exit_json(**result)
def __init__(self, module): self.module = module self.pfsense = PFSenseModule(module) self.certs = self.pfsense.get_elements('cert')
class pfSenseCERT(object): def __init__(self, module): self.module = module self.pfsense = PFSenseModule(module) self.certs = self.pfsense.get_elements('cert') def _find_cert(self, name): found = None i = 0 for cert in self.certs: i = self.pfsense.get_index(cert) if cert.find('descr').text == name: found = cert break return (found, i) def validate_cert(self, cert): lines = cert.splitlines() if lines[0] == '-----BEGIN CERTIFICATE-----' and lines[ -1] == '-----END CERTIFICATE-----': return base64.b64encode(cert) elif re.match('LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t', cert): return cert else: self.module.fail_json( msg='Could not recognize certificate format: %s' % (cert)) def validate_prv(self, prv): lines = prv.splitlines() if lines[0] == '-----BEGIN PRIVATE KEY-----' and lines[ -1] == '-----END PRIVATE KEY-----': return base64.b64encode(prv) elif lines[0] == '-----BEGIN RSA PRIVATE KEY----' and lines[ -1] == '-----END RSA PRIVATE KEY-----': return base64.b64encode(prv) elif re.match('LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0t', prv): return prv elif re.match('LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0t', prv): return prv else: self.module.fail_json( msg='Could not recognize private key format: %s' % (prv)) def add(self, cert): cert_elt, cert_idx = self._find_cert(cert['descr']) changed = False diff = {} stdout = None stderr = None if cert_elt is None: diff['before'] = '' changed = True cert_elt = self.pfsense.new_element('cert') cert['refid'] = self.pfsense.uniqid() self.pfsense.copy_dict_to_element(cert, cert_elt) self.pfsense.root.append(cert_elt) descr = 'ansible pfsensible.core.cert added %s' % (cert['descr']) else: diff['before'] = self.pfsense.element_to_dict(cert_elt) if self.pfsense.copy_dict_to_element(cert, cert_elt): changed = True descr = 'ansible pfsensible.core.cert updated "%s"' % ( cert['descr']) if changed and not self.module.check_mode: self.pfsense.write_config(descr=descr) # cert_import will base64 encode the cert + key and will fix 'certref' for CERTs that reference each other # $cert needs to be an existing reference (particularly 'refid' must be set) before calling cert_import # key and serial are optional arguments. TODO - handle key and serial (dummy, stdout, stderr) = self.pfsense.phpshell(""" init_config_arr(array('cert')); $cert =& lookup_cert('{refid}'); cert_import($cert, '{cert}','{priv}'); print_r($cert); print_r($config['cert']); write_config();""".format( refid=cert_elt.find('refid').text, cert=base64.b64decode(cert_elt.find('crt').text), priv=base64.b64decode(cert_elt.find('prv').text))) diff['after'] = self.pfsense.element_to_dict(cert_elt) self.module.exit_json(changed=changed, diff=diff, stdout=stdout, stderr=stderr) def remove(self, cert): cert_elt, dummy = self._find_cert(cert['descr']) changed = False diff = {} diff['after'] = {} if cert_elt is not None: changed = True diff['before'] = self.pfsense.element_to_dict(cert_elt) self.certs.remove(cert_elt) else: diff['before'] = {} if changed and not self.module.check_mode: self.pfsense.write_config( descr='ansible pfsensible.core.cert removed "%s"' % (cert['descr'])) self.module.exit_json(changed=changed, diff=diff)
class PFSenseModuleAggregate(object): """ module managing pfsense aggregated aliases, rules, rule separators, interfaces and vlans """ def __init__(self, module): self.module = module self.pfsense = PFSenseModule(module) self.pfsense_aliases = PFSenseAliasModule(module, self.pfsense) self.pfsense_interfaces = PFSenseInterfaceModule(module, self.pfsense) self.pfsense_rules = PFSenseRuleModule(module, self.pfsense) self.pfsense_rule_separators = PFSenseRuleSeparatorModule(module, self.pfsense) self.pfsense_vlans = PFSenseVlanModule(module, self.pfsense) def _update(self): run = False cmd = 'require_once("filter.inc");\n' if self.pfsense_vlans.result['changed']: run = True cmd += self.pfsense_vlans.get_update_cmds() if self.pfsense_interfaces.result['changed']: run = True cmd += self.pfsense_interfaces.get_update_cmds() cmd += 'if (filter_configure() == 0) { \n' if self.pfsense_aliases.result['changed']: run = True cmd += 'clear_subsystem_dirty(\'aliases\');\n' if self.pfsense_rules.result['changed'] or self.pfsense_rule_separators.result['changed']: run = True cmd += 'clear_subsystem_dirty(\'filter\');\n' cmd += '}' if run: return self.pfsense.phpshell(cmd) return ('', '', '') def want_rule(self, rule_elt, rules): """ return True if we want to keep rule_elt """ descr = rule_elt.find('descr') interface = rule_elt.find('interface') # probably not a rule if descr is None or interface is None: return True for rule in rules: if rule['state'] == 'absent': continue if rule['name'] == descr.text and self.pfsense.parse_interface(rule['interface']) == interface.text: return True return False def want_rule_separator(self, separator_elt, rule_separators): """ return True if we want to keep separator_elt """ name = separator_elt.find('text').text interface = separator_elt.find('if').text for separator in rule_separators: if separator['state'] == 'absent': continue if separator['name'] != name: continue if self.pfsense.parse_interface(separator['interface']) == interface or interface == 'floatingrules' and separator.get('floating'): return True return False @staticmethod def want_alias(alias_elt, aliases): """ return True if we want to keep alias_elt """ name = alias_elt.find('name') alias_type = alias_elt.find('type') # probably not an alias if name is None or type is None: return True for alias in aliases: if alias['state'] == 'absent': continue if alias['name'] == name.text and alias['type'] == alias_type.text: return True return False def want_interface(self, interface_elt, interfaces): """ return True if we want to keep interface_elt """ descr_elt = interface_elt.find('descr') if descr_elt is not None and descr_elt.text: name = descr_elt.text else: name = interface_elt.tag for interface in interfaces: if interface['state'] == 'absent': continue if interface['descr'] == name: return True return False @staticmethod def want_vlan(vlan_elt, vlans): """ return True if we want to keep vlan_elt """ tag = int(vlan_elt.find('tag').text) interface = vlan_elt.find('if') for vlan in vlans: if vlan['state'] == 'absent': continue if vlan['vlan_id'] == tag and vlan['interface'] == interface.text: return True return False def run_rules(self): """ process input params to add/update/delete all rules """ want = self.module.params['aggregated_rules'] if want is None: return # delete every other rule if required if self.module.params['purge_rules']: todel = [] for rule_elt in self.pfsense_rules.root_elt: if not self.want_rule(rule_elt, want): params = {} params['state'] = 'absent' params['name'] = rule_elt.find('descr').text params['interface'] = self.pfsense.get_interface_display_name(rule_elt.find('interface').text) if rule_elt.find('floating') is not None: params['floating'] = True todel.append(params) for params in todel: self.pfsense_rules.run(params) # generating order if required if self.module.params.get('order_rules'): last_rules = dict() for params in want: if params.get('before') is not None or params.get('after') is not None: self.module.fail_json(msg="You can't use after or before parameters on rules when using order_rules (see {0})".format(params['name'])) if params.get('state') == 'absent': continue if params.get('floating'): key = 'floating' else: key = params['interface'] # first rule on interface if key not in last_rules: params['after'] = 'top' last_rules[key] = params['name'] continue params['after'] = last_rules[key] last_rules[key] = params['name'] # processing aggregated parameters for params in want: self.pfsense_rules.run(params) def run_aliases(self): """ process input params to add/update/delete all aliases """ want = self.module.params['aggregated_aliases'] if want is None: return # processing aggregated parameter for param in want: self.pfsense_aliases.run(param) # delete every other alias if required if self.module.params['purge_aliases']: todel = [] for alias_elt in self.pfsense_aliases.root_elt: if not self.want_alias(alias_elt, want): params = {} params['state'] = 'absent' params['name'] = alias_elt.find('name').text todel.append(params) for params in todel: self.pfsense_aliases.run(params) def run_interfaces(self): """ process input params to add/update/delete all interfaces """ want = self.module.params['aggregated_interfaces'] if want is None: return # processing aggregated parameter for param in want: self.pfsense_interfaces.run(param) # delete every other if required if self.module.params['purge_interfaces']: todel = [] for interface_elt in self.pfsense_interfaces.root_elt: if not self.want_interface(interface_elt, want): params = {} params['state'] = 'absent' descr_elt = interface_elt.find('descr') if descr_elt is not None and descr_elt.text: params['descr'] = descr_elt.text todel.append(params) for params in todel: self.pfsense_interfaces.run(params) def run_rule_separators(self): """ process input params to add/update/delete all separators """ want = self.module.params['aggregated_rule_separators'] if want is None: return # processing aggregated parameter for param in want: self.pfsense_rule_separators.run(param) # delete every other if required if self.module.params['purge_rule_separators']: todel = [] for interface_elt in self.pfsense_rule_separators.separators: for separator_elt in interface_elt: if not self.want_rule_separator(separator_elt, want): params = {} params['state'] = 'absent' params['name'] = separator_elt.find('text').text if interface_elt.tag == 'floatingrules': params['floating'] = True else: params['interface'] = self.pfsense.get_interface_display_name(interface_elt.tag) todel.append(params) for params in todel: self.pfsense_rule_separators.run(params) def run_vlans(self): """ process input params to add/update/delete all vlans """ want = self.module.params['aggregated_vlans'] if want is None: return # processing aggregated parameter for param in want: self.pfsense_vlans.run(param) # delete every other if required if self.module.params['purge_vlans']: todel = [] for vlan_elt in self.pfsense_vlans.root_elt: if not self.want_vlan(vlan_elt, want): params = {} params['state'] = 'absent' params['interface'] = vlan_elt.find('if').text params['vlan_id'] = int(vlan_elt.find('tag').text) todel.append(params) for params in todel: self.pfsense_vlans.run(params) def commit_changes(self): """ apply changes and exit module """ stdout = '' stderr = '' changed = ( self.pfsense_aliases.result['changed'] or self.pfsense_interfaces.result['changed'] or self.pfsense_rules.result['changed'] or self.pfsense_rule_separators.result['changed'] or self.pfsense_vlans.result['changed'] ) if changed and not self.module.check_mode: self.pfsense.write_config(descr='aggregated change') (dummy, stdout, stderr) = self._update() result = {} result['result_aliases'] = self.pfsense_aliases.result['commands'] result['result_interfaces'] = self.pfsense_interfaces.result['commands'] result['result_rules'] = self.pfsense_rules.result['commands'] result['result_rule_separators'] = self.pfsense_rule_separators.result['commands'] result['result_vlans'] = self.pfsense_vlans.result['commands'] result['changed'] = changed result['stdout'] = stdout result['stderr'] = stderr self.module.exit_json(**result)
def __init__(self, module): self.module = module self.pfsense = PFSenseModule(module) self.pfsense_ipsec = PFSenseIpsecModule(module, self.pfsense) self.pfsense_ipsec_proposal = PFSenseIpsecProposalModule(module, self.pfsense) self.pfsense_ipsec_p2 = PFSenseIpsecP2Module(module, self.pfsense)
def __init__(self, module): self.module = module self.pfsense = PFSenseModule(module) self.system = self.pfsense.get_element('system') self.authservers = self.system.findall('authserver') self.diff = {'after': {}, 'before': {}}
class PFSenseAuthserverLDAP(object): def __init__(self, module): self.module = module self.pfsense = PFSenseModule(module) self.system = self.pfsense.get_element('system') self.authservers = self.system.findall('authserver') self.diff = {'after': {}, 'before': {}} def _find_authserver_ldap(self, name): found = None i = 0 for authserver in self.authservers: i = list(self.system).index(authserver) if authserver.find('name').text == name and authserver.find( 'type').text == 'ldap': found = authserver break return (found, i) def add(self, authserver): authserver_elt, i = self._find_authserver_ldap(authserver['name']) changed = False # Replace the text CA name with the caref id authserver['ldap_caref'] = self.pfsense.get_caref(authserver['ca']) # CA is required for SSL/TLS if authserver['ldap_caref'] is None and authserver[ 'ldap_urltype'] != 'TCP - Standard': self.module.fail_json(msg="could not find CA '%s'" % (authserver['ca'])) del authserver['ca'] if authserver_elt is None: changed = True authserver_elt = self.pfsense.new_element('authserver') authserver['refid'] = self.pfsense.uniqid() self.pfsense.copy_dict_to_element(authserver, authserver_elt) self.system.insert(i + 1, authserver_elt) change_descr = 'ansible pfsensible.core.authserver_ldap added %s' % ( authserver['name']) else: self.diff['before'] = self.pfsense.element_to_dict(authserver_elt) changed = self.pfsense.copy_dict_to_element( authserver, authserver_elt) change_descr = 'ansible pfsensible.core.authserver_ldap updated "%s"' % ( authserver['name']) self.diff['after'] = self.pfsense.element_to_dict(authserver_elt) if changed and not self.module.check_mode: self.pfsense.write_config(descr=change_descr) self.module.exit_json(changed=changed, diff=self.diff) def remove(self, authserver): authserver_elt, dummy = self._find_authserver_ldap(authserver['name']) changed = False if authserver_elt is not None: self.diff['before'] = self.pfsense.element_to_dict(authserver_elt) self.authservers.remove(authserver_elt) changed = True if changed and not self.module.check_mode: self.pfsense.write_config( descr='ansible pfsensible.core.authserver_ldap removed "%s"' % (authserver['name'])) self.module.exit_json(changed=changed, diff=self.diff)
def __init__(self, module): self.module = module self.pfsense = PFSenseModule(module) self.cas = self.pfsense.get_elements('ca') self.crls = self.pfsense.get_elements('crl')
class pfSenseCA(object): def __init__(self, module): self.module = module self.pfsense = PFSenseModule(module) self.cas = self.pfsense.get_elements('ca') self.crls = self.pfsense.get_elements('crl') def _find_ca(self, name): found = None i = 0 for ca in self.cas: i = self.pfsense.get_index(ca) if ca.find('descr').text == name: found = ca break return (found, i) def _find_crl(self, caref): found = None i = 0 for crl in self.crls: i = self.pfsense.get_index(crl) if crl.find('caref').text == caref: found = crl break return (found, i) def validate_cert(self, cert): # TODO - Make sure certificate purpose includes CA lines = cert.splitlines() if lines[0] == '-----BEGIN CERTIFICATE-----' and lines[ -1] == '-----END CERTIFICATE-----': return base64.b64encode(cert) elif re.match('LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t', cert): return cert else: self.module.fail_json( msg='Could not recognize certificate format: %s' % (cert)) def validate_crl(self, crl): lines = crl.splitlines() if lines[0] == '-----BEGIN X509 CRL-----' and lines[ -1] == '-----END X509 CRL-----': return base64.b64encode(crl) elif re.match('LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0t', crl): return crl else: self.module.fail_json(msg='Could not recognize CRL format: %s' % (crl)) def add(self, ca): ca_elt, ca_idx = self._find_ca(ca['descr']) changed = False crl = {} diff = {} stdout = None stderr = None if 'crl' in ca: crl['method'] = 'existing' crl['text'] = ca.pop('crl') if ca_elt is None: diff['before'] = '' changed = True ca_elt = self.pfsense.new_element('ca') ca['refid'] = self.pfsense.uniqid() self.pfsense.copy_dict_to_element(ca, ca_elt) self.pfsense.root.append(ca_elt) if 'text' in crl: crl_elt = self.pfsense.new_element('crl') crl['refid'] = self.pfsense.uniqid() crl['descr'] = ca['descr'] + ' CRL' crl['caref'] = ca['refid'] self.pfsense.copy_dict_to_element(crl, crl_elt) self.pfsense.root.append(crl_elt) descr = 'ansible pfsensible.core.ca added %s' % (ca['descr']) else: diff['before'] = self.pfsense.element_to_dict(ca_elt) if 'text' in crl: crl_elt, crl_index = self._find_crl(ca_elt.find('refid').text) if crl_elt is None: changed = True crl_elt = self.pfsense.new_element('crl') crl['refid'] = self.pfsense.uniqid() crl['descr'] = ca['descr'] + ' CRL' crl['caref'] = ca_elt.find('refid').text self.pfsense.copy_dict_to_element(crl, crl_elt) # Add after the existing ca entry self.pfsense.root.insert(ca_idx + 1, crl_elt) else: diff['before']['crl'] = crl_elt.find('text').text changed = self.pfsense.copy_dict_to_element(crl, crl_elt) if self.pfsense.copy_dict_to_element(ca, ca_elt): changed = True descr = 'ansible pfsensible.core.ca updated "%s"' % (ca['descr']) if changed and not self.module.check_mode: self.pfsense.write_config(descr=descr) # ca_import will base64 encode the cert + key and will fix 'caref' for CAs that reference each other # $ca needs to be an existing reference (particularly 'refid' must be set) before calling ca_import # key and serial are optional arguments. TODO - handle key and serial (dummy, stdout, stderr) = self.pfsense.phpshell(""" init_config_arr(array('ca')); $ca =& lookup_ca('{refid}'); ca_import($ca, '{cert}'); print_r($ca); print_r($config['ca']); write_config();""".format(refid=ca_elt.find('refid').text, cert=base64.b64decode( ca_elt.find('crt').text))) if 'text' in crl: self.pfsense.phpshell(""" require_once("openvpn.inc"); openvpn_refresh_crls(); require_once("vpn.inc"); vpn_ipsec_configure();""") diff['after'] = self.pfsense.element_to_dict(ca_elt) if 'text' in crl: diff['after']['crl'] = crl['text'] self.module.exit_json(changed=changed, diff=diff, stdout=stdout, stderr=stderr) def remove(self, ca): ca_elt, dummy = self._find_ca(ca['descr']) changed = False diff = {} diff['after'] = {} if ca_elt is not None: changed = True diff['before'] = self.pfsense.element_to_dict(ca_elt) crl_elt, dummy = self._find_crl(ca_elt.find('refid').text) self.cas.remove(ca_elt) if crl_elt is not None: diff['before']['crl'] = crl_elt.find('text').text self.crls.remove(crl_elt) else: diff['before'] = {} if changed and not self.module.check_mode: self.pfsense.write_config( descr='ansible pfsensible.core.ca removed "%s"' % (ca['descr'])) self.module.exit_json(changed=changed, diff=diff)
class pfSenseUser(object): def __init__(self, module): self.module = module self.pfsense = PFSenseModule(module) self.system = self.pfsense.get_element('system') self.users = self.system.findall('user') self.groups = self.system.findall('group') self.diff = {} self.change_descr = '' def _find_user(self, name): found = None i = 0 for user in self.users: if user.find('name').text == name: found = user break i += 1 return (found, i) def _find_group(self, name): found = None i = 0 for group_elt in self.groups: if group_elt.find('name').text == name: found = group_elt break i += 1 return (found, i) def _find_groups_for_uid(self, uid): groups = [] for group_elt in self.groups: for member_elt in group_elt.findall('member'): if member_elt.text == uid: groups.append(group_elt.find('name').text) return groups def _find_last_user_idx(self): found = False i = 0 for elt in self.system: if elt.tag == 'user': i += 1 found = True if not found: i += 1 return i def _nextuid(self): nextuid_elt = self.system.find('nextuid') nextuid = nextuid_elt.text nextuid_elt.text = str(int(nextuid) + 1) return nextuid def _format_diff_priv(self, priv): if isinstance(priv, str): return [priv] else: return priv def _validate_password(self, user): # if re.match(r'\$2b\$', user['password']): user['bcrypt-hash'] = user['password'] # else: # self.module.fail_json(msg='Password (%s) does not appear to be a bcrypt hash' % user['password']) # del user['password'] def add(self, user): changed = False stdout = None stderr = None mod_groups = [] # Allow authorizedkeys to be clear or base64 encoded if 'authorizedkeys' in user and 'ssh-' in user['authorizedkeys']: user['authorizedkeys'] = base64.b64encode(user['authorizedkeys']) user_elt, user_idx = self._find_user(user['name']) if user_elt is None: changed = True self.diff['before'] = {} if 'password' in user: self._validate_password(user) else: self.module.fail_json( msg='Password is required when adding a user') if 'uid' not in user: user['uid'] = self._nextuid() self.diff['after'] = user user_elt = self.pfsense.new_element('user') self.pfsense.copy_dict_to_element(user, user_elt) self.system.insert(self._find_last_user_idx(), user_elt) self.change_descr = 'ansible pfsensible.core.user added %s' % ( user['name']) else: if 'password' in user: self._validate_password(user) self.diff['before'] = self.pfsense.element_to_dict(user_elt) if 'priv' in self.diff['before']: self.diff['before']['priv'] = self._format_diff_priv( self.diff['before']['priv']) changed = self.pfsense.copy_dict_to_element(user, user_elt) self.diff['after'] = self.pfsense.element_to_dict(user_elt) if 'priv' in self.diff['after']: self.diff['after']['priv'] = self._format_diff_priv( self.diff['after']['priv']) self.change_descr = 'ansible pfsensible.core.user updated "%s"' % ( user['name']) # Handle group member element - need uid set or retrieved above if 'groups' in user: uid = user_elt.find('uid').text # Get current group membership self.diff['before']['groups'] = self._find_groups_for_uid(uid) # Add user to groups if needed for group in user['groups']: group_elt, group_idx = self._find_group(group) if group_elt is None: self.module.fail_json(msg='Group (%s) does not exist' % group) member_found = False for member_elt in group_elt.findall('member'): if member_elt.text == uid: member_found = True if not member_found: changed = True mod_groups.append(group) group_elt.append(self.pfsense.new_element('member', uid)) # Remove user from groups if needed for group in self.diff['before']['groups']: if group not in user['groups']: group_elt, group_idx = self._find_group(group) if group_elt is None: self.module.fail_json(msg='Group (%s) does not exist' % group) for member_elt in group_elt.findall('member'): if member_elt.text == uid: changed = True mod_groups.append(group) group_elt.remove(member_elt) break # Groups are not stored in the user element self.diff['after']['groups'] = user.pop('groups') # Decode keys for diff for k in self.diff: if 'authorizedkeys' in self.diff[k]: self.diff[k]['authorizedkeys'] = base64.b64decode( self.diff[k]['authorizedkeys']) if changed and not self.module.check_mode: self.pfsense.write_config(descr=self.change_descr) (dummy, stdout, stderr) = self.pfsense.phpshell( USER_PHP_COMMAND_SET.format(idx=user_idx, mod_groups=mod_groups)) self.module.exit_json(changed=changed, diff=self.diff, stdout=stdout, stderr=stderr) def remove(self, user): user_elt, user_idx = self._find_user(user['name']) changed = False stdout = None stderr = None uid = user_elt.find('uid').text mod_groups = [] self.diff['after'] = {} if user_elt is not None: changed = True self.diff['before'] = self.pfsense.element_to_dict(user_elt) # Get current group membership self.diff['before']['groups'] = self._find_groups_for_uid(uid) # Remove user from groups if needed for group in self.diff['before']['groups']: group_elt, group_idx = self._find_group(group) if group_elt is None: self.module.fail_json(msg='Group (%s) does not exist' % group) for member_elt in group_elt.findall('member'): if member_elt.text == uid: mod_groups.append(group) group_elt.remove(member_elt) break self.system.remove(user_elt) if changed and not self.module.check_mode: (dummy, stdout, stderr) = self.pfsense.phpshell( USER_PHP_COMMAND_DEL.format(cmd='del', idx=user_idx, mod_groups=mod_groups)) self.pfsense.write_config( descr='ansible pfsensible.core.user removed "%s"' % (user['name'])) self.module.exit_json(changed=changed, diff=self.diff, stdout=stdout, stderr=stderr)