def test_action(self): action = Action() # Defaults self.assertEqual(action.action, 'allow') self.assertEqual(action.scan_detection, 'undefined') action.action = 'discard' self.assertEqual(action.action, 'discard') action.action = 'foo' self.assertEqual(action.action, 'foo') action.deep_inspection = True action.file_filtering = False action.dos_protection = True action.scan_detection = 'on' action.vpn = 'http://1.1.1.1' action.mobile_vpn = True self.assertTrue(action.deep_inspection) self.assertFalse(action.file_filtering) self.assertTrue(action.dos_protection) self.assertEqual(action.scan_detection, 'on') self.assertEqual(action.vpn, 'http://1.1.1.1') self.assertTrue(action.mobile_vpn) self.assertIsNone(action.user_response) self.assertFalse(action.connection_tracking_options.mss_enforced) self.assertEqual(action.connection_tracking_options.timeout, -1) mini, maxi = action.connection_tracking_options.mss_enforced_min_max self.assertEqual(mini, 0) self.assertEqual(maxi, 0) action.connection_tracking_options.state = 'normal' action.connection_tracking_options.timeout = 60 action.connection_tracking_options.mss_enforced_min_max = (1400, 1450) action.connection_tracking_options.mss_enforced = True self.assertEqual(action.connection_tracking_options.state, 'normal') self.assertEqual(action.connection_tracking_options.timeout, 60) mini, maxi = action.connection_tracking_options.mss_enforced_min_max self.assertEqual(mini, 1400) self.assertEqual(maxi, 1450) self.assertTrue(action.connection_tracking_options.mss_enforced) o = action() for k, v in o.items(): self.assertEqual(k, 'action') self.assertDictEqual(v, action.data) action = Action({'foo': 'bar'}) self.assertDictEqual(action.data, {'foo': 'bar'})
def _get_action_6_5(self, action): """ Get the action field for a rule. In SMC 6.6 actions have to be in list format whereas in SMC < 6.6 they were string. :param str,list action: provided action in create constructor :rtype: Action :raises CreateRuleFailed: invalid rule based on rule """ if isinstance(action, Action): rule_action = action else: rule_action = Action() if isinstance(action, str): rule_action.action = action else: raise CreateRuleFailed("Action specified should be a str " "rule type; action: {}".format(action)) valid_action = False if isinstance(rule_action.action, list): valid_action = all(_action in self._actions for _action in rule_action.action) else: valid_action = rule_action.action in self._actions if not valid_action: raise CreateRuleFailed("Action specified is not valid for this " "rule type; action: {}".format( rule_action.action)) return rule_action
def action(self): """ Action for this rule. :rtype: Action """ return Action(self)
def action(self): """ Action for this rule. :return: :py:class:`smc.policy.rule_elements.Action` """ return Action(self.data.get('action'), self.actions)
def create(self, name, sources=None, destinations=None, services=None, action='allow', is_disabled=False, logical_interfaces=None): """ Create an Ethernet rule :param str name: name of rule :param list sources: list of source href's :param list destinations: list of destination href's :param list services: list of service href's :param list logical_interfaces: logical interfaces by name :param str action: \|allow\|continue\|discard\|refuse\|blacklist :param boolean is_disabled: whether to disable rule or not :raises: :py:class:`smc.api.exceptions.MissingReuqiredInput` when options are specified the need additional setting, i.e. use_vpn action requires a vpn policy be specified. :raises: :py:class:`smc.api.exceptions.CreateRuleFailed`: rule creation failure :return: str href: href of new rule """ rule_values = _rule_common(sources, destinations, services) rule_values.update(name=name) rule_values.update(is_disabled=is_disabled) rule_action = Action(actions=self.actions) rule_action.action = action rule_values.update(rule_action()) rule_values.update(_rule_l2_common(logical_interfaces)) return prepared_request(CreateRuleFailed, href=self.href, json=rule_values).create().href
def test_L2FW_validate_fw_rule_creation(self): policy = Layer2Policy.create( name='layer2foo', template='Layer 2 Firewall Inspection Template') self.assertIsInstance(policy, Layer2Policy) policy.layer2_ipv4_access_rules.create(name='myrule', sources='any', action=Action()) for rule in policy.layer2_ipv4_access_rules.all(): self.assertIsInstance(rule, IPv4Layer2Rule) self.assertEqual(rule.name, 'myrule') self.assertEqual(rule.action.action, 'allow') # Rule with incorrect action with self.assertRaises(CreateRuleFailed): policy.layer2_ipv4_access_rules.create(name='myrule', sources='any', action='duh') # Search should return right object time rules = policy.search_rule('myrule') self.assertIsInstance(rules, list) self.assertIsInstance(rules[0], IPv4Layer2Rule) rules[0].delete() # Rule with non-existant logical interface with self.assertRaises(MissingRequiredInput): policy.layer2_ipv4_access_rules.create(name='myrule', logical_interfaces=['foo']) # Rule position policy.layer2_ipv4_access_rules.create(name='rule2', sources='any') # Added to top policy.layer2_ipv4_access_rules.create(name='rule', sources='any') # Added to top policy.layer2_ipv4_access_rules.create(name='pos2rule', sources='any', add_pos=2) for num, rule in enumerate(policy.layer2_ipv4_access_rules.all()): if rule.name == 'rule': self.assertEqual(num + 1, 1) elif rule.name == 'pos2rule': self.assertEqual(num + 1, 2) elif rule.name == 'rule2': self.assertEqual(num + 1, 3) # Add to end policy.layer2_ipv4_access_rules.create(name='bottom', sources='any', add_pos=100) for num, rule in enumerate(policy.layer2_ipv4_access_rules.all()): if rule.name == 'bottom': self.assertEqual(num + 1, 4) policy.delete()
def create(self, name, sources=None, destinations=None, services=None, action='allow', is_disabled=False, vpn_policy=None, **kwargs): """ Create a layer 3 firewall rule :param str name: name of rule :param list source: source/s for rule, in href format :param list destination: destinations, in href format :param list service: service/s, in href format :param str action: allow|continue|discard|refuse|enforce_vpn|apply_vpn|blacklist (default: allow) :param str: vpn_policy: vpn policy name; required for enforce_vpn and apply_vpn actions :raises: :py:class:`smc.api.exceptions.MissingRequiredInput` when options are specified the need additional setting, i.e. use_vpn action requires a vpn policy be specified. :raises: :py:class:`smc.api.exceptions.CreateRuleFailed`: rule creation failure :return: str href: href of new rule """ rule_values = _rule_common(sources, destinations, services) rule_values.update(name=name) rule_action = Action(actions=self.actions) rule_action.action = action if rule_action.action in ['apply_vpn', 'enforce_vpn', 'forward_vpn']: if vpn_policy is None: raise MissingRequiredInput('A VPN policy must be specified when ' 'rule action has a VPN action') try: vpn = VPNPolicy(vpn_policy).href rule_action.vpn = vpn except ElementNotFound: raise MissingRequiredInput('Cannot find VPN policy specified: {}, ' .format(vpn_policy)) rule_values.update(rule_action()) log_options = LogOptions() auth_options = AuthenticationOptions() rule_values.update(log_options()) rule_values.update(auth_options()) rule_values.update(is_disabled=is_disabled) return prepared_request(CreateRuleFailed, href=self.href, json=rule_values).create().href
def test_L2FW_validate_fw_ethernet_rule_creation(self): policy = Layer2Policy.create( name='layer2foo', template='Layer 2 Firewall Inspection Template') self.assertIsInstance(policy, Layer2Policy) policy.layer2_ethernet_rules.create(name='myethernetrule', sources='any', destinations='any', services='any', action=Action()) for rule in policy.layer2_ethernet_rules.all(): self.assertIsInstance(rule, EthernetRule) self.assertEqual(rule.action.action, 'allow') self.assertEqual(rule.name, 'myethernetrule') rule.delete() # Rule position policy.layer2_ethernet_rules.create(name='rule2', sources='any') # Added to top # Added to top, rule2 becomes pos 2 policy.layer2_ethernet_rules.create(name='rule', sources='any') policy.layer2_ethernet_rules.create(name='pos2rule', sources='any', add_pos=2) for num, rule in enumerate(policy.layer2_ethernet_rules.all()): if rule.name == 'rule': self.assertEqual(num + 1, 1) elif rule.name == 'pos2rule': self.assertEqual(num + 1, 2) elif rule.name == 'rule2': self.assertEqual(num + 1, 3) # Add to end policy.layer2_ethernet_rules.create(name='bottom', sources='any', add_pos=100) for num, rule in enumerate(policy.layer2_ethernet_rules.all()): if rule.name == 'bottom': self.assertEqual(num + 1, 4) policy.delete()
def test_modify_rules(self): policy = FirewallPolicy.create(name='myfoopolicy', template='Firewall Inspection Template') Host.create(name='foobar', address='1.1.1.1') host = Host('foobar') # No action, default to Allow, position set, but no rules so added # normally policy.fw_ipv4_access_rules.create(name='myrule', sources=[host], add_pos=10) for rule in policy.fw_ipv4_access_rules.all(): if rule.name == 'myrule': self.assertEqual(rule.action.action, 'allow') self.assertIsInstance(rule.authentication_options, AuthenticationOptions) self.assertIsInstance(rule.options, LogOptions) self.assertFalse(rule.is_disabled) self.assertEqual(rule.parent_policy.name, 'myfoopolicy') self.assertTrue(rule.destinations.is_none) self.assertTrue(rule.services.is_none) self.assertFalse(rule.services.all()) for source in rule.sources.all(): self.assertEqual(source.name, 'foobar') rule.disable() rule.comment = 'mycomment' rule.services.set_any() rule.save() self.assertEqual(rule.comment, 'mycomment') self.assertTrue(rule.is_disabled) rule.enable() rule.save() self.assertFalse(rule.is_disabled) self.assertTrue(rule.services.is_any) rule.delete() Host('foobar').delete() policy.fw_ipv4_access_rules.create(name='myrule', sources='any', destinations=[Host('badhost')], action=Action()) # Will be returned as type "Element" engine = Layer3Firewall.create(name='tmpfw', mgmt_ip='1.1.1.1', mgmt_network='1.1.1.0/24') Host.create(name='boo', address='12.12.12.12') for rule in policy.fw_ipv4_access_rules.all(): if rule.name == 'myrule': self.assertTrue(rule.destinations.is_none) self.assertTrue(rule.action.action == 'allow') self.assertTrue(rule.sources.is_any) # Host doesn't exist, destinations will remain none rule.destinations.add(Host('blah')) rule.save() self.assertTrue(rule.destinations.is_none) rule.destinations.add_many([Host('boo'), engine]) rule.save() dests = list(rule.destinations.all()) self.assertTrue(len(dests) == 2) for x in rule.destinations.all(): self.assertIsInstance(x, Element) self.assertIn(x.name, ['tmpfw', 'boo']) # Test Match Expression DomainName.create('lepages.net') Zone.create('MyExZone') match = MatchExpression.create(name='mymatch', network_element=Host('boo'), domain_name=DomainName('lepages.net'), zone=Zone('MyExZone')) policy.fw_ipv4_access_rules.create(name='matchex', sources=[match], destinations='any', services='any') rule = policy.search_rule('matchex')[0] self.assertTrue(len(rule.sources.all()[0].values()) == 3) for source in rule.sources.all(): for values in source.values(): self.assertIn(values.name, ['boo', 'lepages.net', 'MyExZone']) # Test after and before rule position options # Need rule tag for rule we are inserting around rule = policy.search_rule('matchex')[0] policy.fw_ipv4_access_rules.create(name='ruleafter', after=rule.tag) policy.fw_ipv4_access_rules.create(name='rulebefore', before=rule.tag) gen = policy.fw_ipv4_access_rules.all() for x in gen: if x.name == 'rulebefore': break matchex = next(gen) ruleafter = next(gen) self.assertEqual(matchex.name, 'matchex') self.assertEqual(ruleafter.name, 'ruleafter') # Add a jump rule with self.assertRaises(MissingRequiredInput): policy.fw_ipv4_access_rules.create( name='jumprule', action='jump', sub_policy=FirewallSubPolicy('blahblah')) FirewallSubPolicy.create('subfoo') policy.fw_ipv4_access_rules.create( name='jumprule', action='jump', sub_policy=FirewallSubPolicy('subfoo')) rule = policy.search_rule('jumprule')[0] self.assertEqual(rule.action.action, 'jump') self.assertEqual(rule.action.sub_policy.href, FirewallSubPolicy('subfoo').href) policy.delete() DomainName('lepages.net').delete() Zone('MyExZone').delete() Host('boo').delete() time.sleep(3) engine.delete()
def exec_module(self, **kwargs): state = kwargs.pop('state', 'present') for name, value in kwargs.items(): setattr(self, name, value) changed = False try: # Now that we have valid option settings, we can hit the db if self.policy: policy = FirewallPolicy.get(self.policy) else: policy = FirewallSubPolicy.get(self.sub_policy) if state == 'present': for rule in self.rules: try: validate_rule_syntax(rule) except Exception as e: self.fail(msg=str(e)) self.cache = Cache() for rule in self.rules: # Resolve elements if they exist, calls to SMC could happen here if 'sources' in rule: self.field_resolver(rule.get('sources'), rule_targets) if 'destinations' in rule: self.field_resolver(rule.get('destinations'), rule_targets) if 'services' in rule: self.field_resolver(rule.get('services'), service_targets) if 'vpn_policy' in rule: self.cache._add_entry('vpn', rule.get('vpn_policy')) if 'sub_policy' in rule: self.cache._add_entry('sub_ipv4_fw_policy', rule.get('sub_policy')) if 'authentication_options' in rule: auth = rule['authentication_options'] if auth.get('require_auth'): for method in auth.get('methods'): self.cache._add_entry('authentication_service', method) for accounts in ('users', 'groups'): self.cache._add_user_entries(accounts, auth.get(accounts, [])) if self.cache.missing: self.fail(msg='Missing required elements that are referenced in this ' 'configuration: %s' % self.cache.missing) if self.check_mode: return self.results for rule in self.rules: rule_dict = {} if 'log_options' in rule: log_options = LogOptions() _log = rule['log_options'] for name, value in log_options.items(): if name not in _log: log_options.pop(name) log_options.update(rule.get('log_options', {})) rule_dict.update(log_options=log_options) if 'connection_tracking' in rule: connection_tracking = ConnectionTracking() _ct = rule['connection_tracking'] for name, value in connection_tracking.items(): if name not in _ct: connection_tracking.pop(name) connection_tracking.update(rule.get('connection_tracking',{})) rule_dict.update(connection_tracking=connection_tracking) action = Action() # If no action, set to default based on version if 'action' not in rule: action.action = 'allow' if not is_sixdotsix_compat() else ['allow'] else: action.action = rule.get('action') if 'inspection_options' in rule: _inspection = rule['inspection_options'] for option in inspection_options: if option in _inspection: action[option] = _inspection.get(option) if 'authentication_options' in rule: _auth_options = rule['authentication_options'] auth_options = AuthenticationOptions() if _auth_options.get('require_auth'): auth_options.update(methods=[ self.get_value('authentication_service', m).href for m in _auth_options.get('methods', [])], require_auth=True) auth_options.update(users=[entry.href for entry in self.cache.get_type('user_element')]) rule_dict.update(authentication_options=auth_options) rule_dict.update(action=action) for field in ('sources', 'destinations', 'services'): rule_dict[field] = self.get_values(rule.get(field, None)) rule_dict.update( vpn_policy=self.get_value('vpn', rule.get('vpn_policy')), sub_policy=self.get_value('sub_ipv4_fw_policy', rule.get('sub_policy')), mobile_vpn=rule.get('mobile_vpn', False)) if 'comment' in rule: rule_dict.update(comment=rule.get('comment')) rule_dict.update( name=rule.get('name'), is_disabled=rule.get('is_disabled', False)) if 'tag' not in rule: # If no tag is present, this is a create rule_dict.update( before=rule.get('add_before'), after=rule.get('add_after')) rule = policy.fw_ipv4_access_rules.create(**rule_dict) changed = True self.results['state'].append({ 'rule': rule.name, 'type': rule.typeof, 'action': 'created'}) else: # Modify as rule has 'tag' defined. Fetch the rule first # by it's tag reference, skip if tag not found target_rule = self.rule_by_tag(policy, rule.get('tag')) if not target_rule: continue changes = compare_rules(target_rule, rule_dict) # Changes have already been merged if any if rule.get('add_after', None): rule_at_pos = self.rule_by_tag(policy, rule.get('add_after')) if rule_at_pos: target_rule.move_rule_after(rule_at_pos) changes.append('add_after') elif rule.get('add_before', None): rule_at_pos = self.rule_by_tag(policy, rule.get('add_before')) if rule_at_pos: target_rule.move_rule_before(rule_at_pos) changes.append('add_before') elif changes: target_rule.save() if changes: changed = True self.results['state'].append({ 'rule': target_rule.name, 'type': target_rule.typeof, 'action': 'modified', 'changes': changes}) elif state == 'absent': for rule in self.rules: if 'tag' in rule: target_rule = self.rule_by_tag(policy, rule.get('tag')) if target_rule: target_rule.delete() changed = True self.results['state'].append({ 'rule': target_rule.name, 'type': target_rule.typeof, 'action': 'deleted'}) except SMCException as err: self.fail(msg=str(err), exception=traceback.format_exc()) self.results['changed'] = changed return self.results
def create(self, name, sources=None, destinations=None, services=None, action='allow', is_disabled=False, logical_interfaces=None, add_pos=None, after=None, before=None, comment=None): """ Create an Ethernet rule :param str name: name of rule :param sources: source/s for rule :type sources: list[str, Element] :param destinations: destination/s for rule :type destinations: list[str, Element] :param services: service/s for rule :type services: list[str, Element] :param str action: \|allow\|continue\|discard\|refuse\|blacklist :param bool is_disabled: whether to disable rule or not :param list logical_interfaces: logical interfaces by name :param int add_pos: position to insert the rule, starting with position 1. If the position value is greater than the number of rules, the rule is inserted at the bottom. If add_pos is not provided, rule is inserted in position 1. Mutually exclusive with ``after`` and ``before`` params. :param str after: Rule tag to add this rule after. Mutually exclusive with ``add_pos`` and ``before`` params. :param str before: Rule tag to add this rule before. Mutually exclusive with ``add_pos`` and ``after`` params. :raises MissingReuqiredInput: when options are specified the need additional setting, i.e. use_vpn action requires a vpn policy be specified. :raises CreateRuleFailed: rule creation failure :return: newly created rule :rtype: EthernetRule """ rule_values = self.update_targets(sources, destinations, services) rule_values.update(name=name, comment=comment) rule_values.update(is_disabled=is_disabled) if isinstance(action, Action): rule_action = action else: rule_action = Action() rule_action.action = action if not rule_action.action in self._actions: raise CreateRuleFailed('Action specified is not valid for this ' 'rule type; action: {}'.format( rule_action.action)) rule_values.update(action=rule_action.data) rule_values.update(self.update_logical_if(logical_interfaces)) params = None href = self.href if add_pos is not None: href = self.add_at_position(add_pos) else: params = self.add_before_after(before, after) return ElementCreator(self.__class__, exception=CreateRuleFailed, href=href, params=params, json=rule_values)
def create(self, name, sources=None, destinations=None, services=None, action='allow', log_options=None, authentication_options=None, connection_tracking=None, is_disabled=False, vpn_policy=None, mobile_vpn=False, add_pos=None, after=None, before=None, sub_policy=None, comment=None, **kw): """ Create a layer 3 firewall rule :param str name: name of rule :param sources: source/s for rule :type sources: list[str, Element] :param destinations: destination/s for rule :type destinations: list[str, Element] :param services: service/s for rule :type services: list[str, Element] :param action: allow,continue,discard,refuse,enforce_vpn, apply_vpn,forward_vpn, blacklist (default: allow) :type action: Action or str :param LogOptions log_options: LogOptions object :param ConnectionTracking connection_tracking: custom connection tracking settings :param AuthenticationOptions authentication_options: options for auth if any :param PolicyVPN,str vpn_policy: policy element or str href; required for enforce_vpn, use_vpn and apply_vpn actions :param bool mobile_vpn: if using a vpn action, you can set mobile_vpn to True and omit the vpn_policy setting if you want this VPN to apply to any mobile VPN based on the policy VPN associated with the engine :param str,Element sub_policy: sub policy required when rule has an action of 'jump'. Can be the FirewallSubPolicy element or href. :param int add_pos: position to insert the rule, starting with position 1. If the position value is greater than the number of rules, the rule is inserted at the bottom. If add_pos is not provided, rule is inserted in position 1. Mutually exclusive with ``after`` and ``before`` params. :param str after: Rule tag to add this rule after. Mutually exclusive with ``add_pos`` and ``before`` params. :param str before: Rule tag to add this rule before. Mutually exclusive with ``add_pos`` and ``after`` params. :param str comment: optional comment for this rule :raises MissingRequiredInput: when options are specified the need additional setting, i.e. use_vpn action requires a vpn policy be specified. :raises CreateRuleFailed: rule creation failure :return: the created ipv4 rule :rtype: IPv4Rule """ rule_values = self.update_targets(sources, destinations, services) rule_values.update(name=name, comment=comment) if isinstance(action, Action): rule_action = action else: rule_action = Action() rule_action.action = action if not rule_action.action in self._actions: raise CreateRuleFailed('Action specified is not valid for this ' 'rule type; action: {}'.format( rule_action.action)) if rule_action.action in ('apply_vpn', 'enforce_vpn', 'forward_vpn'): if vpn_policy is None and not mobile_vpn: raise MissingRequiredInput( 'You must either specify a vpn_policy or set ' 'mobile_vpn when using a rule with a VPN action') if mobile_vpn: rule_action.mobile_vpn = True else: try: vpn = element_resolver(vpn_policy) # VPNPolicy rule_action.vpn = vpn except ElementNotFound: raise MissingRequiredInput( 'Cannot find VPN policy specified: {}, '.format( vpn_policy)) elif rule_action.action == 'jump': try: rule_action.sub_policy = element_resolver(sub_policy) except ElementNotFound: raise MissingRequiredInput( 'Cannot find sub policy specified: {} '.format(sub_policy)) #rule_values.update(action=rule_action.data) log_options = LogOptions() if not log_options else log_options if connection_tracking is not None: rule_action.connection_tracking_options.update( **connection_tracking) auth_options = AuthenticationOptions() if not authentication_options \ else authentication_options rule_values.update(action=rule_action.data, options=log_options.data, authentication_options=auth_options.data, is_disabled=is_disabled) params = None href = self.href if add_pos is not None: href = self.add_at_position(add_pos) elif before or after: params = self.add_before_after(before, after) return ElementCreator(self.__class__, exception=CreateRuleFailed, href=href, params=params, json=rule_values)
def add_policy(self): """ If a client AMI was specified when building a new VPC, this will add rules to allow inbound access to the AMI. This could be extended to more generically support VPN rules. """ if not self.firewall_policy: self.firewall_policy = 'AWS_Default' # Policy not specified, use the default, or check if hidden setting was specified try: FirewallPolicy.create(name='AWS_Default', template='Firewall Inspection Template') except CreatePolicyFailed: pass # Already exists policy = FirewallPolicy(self.firewall_policy) # Create the access rule for the network options = LogOptions() options.log_accounting_info_mode = True options.log_level = 'stored' options.application_logging = 'enforced' options.user_logging = 'enforced' action = Action() action.deep_inspection = True action.file_filtering = False outbound_rule = policy.search_rule('AWS outbound access rule') if not outbound_rule: # Generic outbound access rule policy.fw_ipv4_access_rules.create( name='AWS outbound access rule', sources=[Alias('$$ Interface ID 1.net')], destinations='any', services='any', action=action, log_options=options) if self.aws_ami_ip and self.nat_ports: dest_port = self.nat_ports.get('dest_port') redirect_port = self.nat_ports.get('redirect_port') services = list( TCPService.objects.filter(dest_port)) # @UndefinedVariable # Ignore services with protocol agents so we skip SSM service = next( ([service] for service in services if not service.protocol_agent), []) if not service: service = [ TCPService.create(name='aws_tcp{}'.format(dest_port), min_dst_port=dest_port) ] # Create the access rule for the client policy.fw_ipv4_access_rules.create( name=self.name, sources='any', destinations=[Alias('$$ Interface ID 0.ip')], services=service, action='allow', log_options=options) policy.fw_ipv4_nat_rules.create( name=self.name, sources='any', destinations=[Alias('$$ Interface ID 0.ip')], services=service, static_dst_nat=self.aws_ami_ip, static_dst_nat_ports=(dest_port, redirect_port), used_on=self.engine.href)
def create(self, name, sources=None, destinations=None, services=None, action='allow', log_options=None, is_disabled=False, vpn_policy=None, add_pos=None, after=None, before=None, sub_policy=None, comment=None, **kw): """ Create a layer 3 firewall rule :param str name: name of rule :param sources: source/s for rule :type sources: list[str, Element] :param destinations: destination/s for rule :type destinations: list[str, Element] :param services: service/s for rule :type services: list[str, Element] :param action: allow,continue,discard,refuse,enforce_vpn, apply_vpn,blacklist (default: allow) :type action: Action or str :param LogOptions log_options: LogOptions object :param str: vpn_policy: vpn policy name; required for enforce_vpn and apply_vpn actions :param str,Element sub_policy: sub policy required when rule has an action of 'jump'. Can be the FirewallSubPolicy element or href. :param int add_pos: position to insert the rule, starting with position 1. If the position value is greater than the number of rules, the rule is inserted at the bottom. If add_pos is not provided, rule is inserted in position 1. Mutually exclusive with ``after`` and ``before`` params. :param str after: Rule tag to add this rule after. Mutually exclusive with ``add_pos`` and ``before`` params. :param str before: Rule tag to add this rule before. Mutually exclusive with ``add_pos`` and ``after`` params. :param str comment: optional comment for this rule :raises MissingRequiredInput: when options are specified the need additional setting, i.e. use_vpn action requires a vpn policy be specified. :raises CreateRuleFailed: rule creation failure :return: the created ipv4 rule :rtype: IPv4Rule """ rule_values = self.update_targets(sources, destinations, services) rule_values.update(name=name, comment=comment) if isinstance(action, Action): rule_action = action else: rule_action = Action() rule_action.action = action if not rule_action.action in self._actions: raise CreateRuleFailed('Action specified is not valid for this ' 'rule type; action: {}'.format( rule_action.action)) if rule_action.action in ['apply_vpn', 'enforce_vpn', 'forward_vpn']: if vpn_policy is None: raise MissingRequiredInput( 'A VPN policy must be specified when ' 'rule action has a VPN action') try: vpn = PolicyVPN(vpn_policy).href rule_action.vpn = vpn except ElementNotFound: raise MissingRequiredInput( 'Cannot find VPN policy specified: {}, '.format( vpn_policy)) elif rule_action.action in ['jump']: try: rule_action.sub_policy = element_resolver(sub_policy) except ElementNotFound: raise MissingRequiredInput( 'Cannot find sub policy specified: {} '.format(sub_policy)) rule_values.update(action=rule_action.data) if log_options is None: log_options = LogOptions() auth_options = AuthenticationOptions() rule_values.update(options=log_options.data) rule_values.update(authentication_options=auth_options.data) rule_values.update(is_disabled=is_disabled) params = None href = self.href if add_pos is not None: href = self.add_at_position(add_pos) elif before or after: params = self.add_before_after(before, after) return SubElementCreator(self.__class__, CreateRuleFailed, href=href, params=params, json=rule_values)