コード例 #1
0
    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
コード例 #2
0
ファイル: ipsec_aggregate.py プロジェクト: wungad/core
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)
コード例 #3
0
 def __init__(self, module):
     self.module = module
     self.pfsense = PFSenseModule(module)
     self.system = self.pfsense.get_element('system')
     self.groups = self.system.findall('group')
コード例 #4
0
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)
コード例 #5
0
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)
コード例 #6
0
 def __init__(self, module):
     self.module = module
     self.pfsense = PFSenseModule(module)
     self.certs = self.pfsense.get_elements('cert')
コード例 #7
0
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)
コード例 #8
0
ファイル: aggregate.py プロジェクト: wungad/core
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)
コード例 #9
0
 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)
コード例 #10
0
 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': {}}
コード例 #11
0
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)
コード例 #12
0
ファイル: ca.py プロジェクト: wungad/core
 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')
コード例 #13
0
ファイル: ca.py プロジェクト: wungad/core
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)
コード例 #14
0
ファイル: user.py プロジェクト: cklee288/core
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)