def _get_expected_rules(self, networks, firewall_policy): """Builds a FirewallRules object with the rules that should be defined. Args: networks (list): A list of networks on the project that the policy applies to. firewall_policy (list): A list of firewall rules that should be configured on the project networks. Returns: fe.FirewallRules: A new FirewallRules object with the expected policy. Raises: EnforcementError: Raised if one or more firewall rules in the policy are invalid. """ expected_rules = fe.FirewallRules(self.project_id) try: for network_name in networks: expected_rules.add_rules( firewall_policy, network_name=network_name) except fe.InvalidFirewallRuleError as e: raise EnforcementError(STATUS_ERROR, 'error adding the expected ' 'firewall rules from the policy: %s' % e) return expected_rules
def _get_current_fw_rules(self, add_rule_callback=None): """Create a new FirewallRules object with the current rules. Args: add_rule_callback (Callable): A callback function that checks whether a firewall rule should be applied. If the callback returns False, that rule will not be modified. Returns: fe.FirewallRules: A new FirewallRules object with the current rules added to it. Raises: ProjectDeletedError: Raised if the project has been deleted. ComputeApiDisabledError: Raised if the Compute API is not enabled on the project. EnforcementError: Raised if there are any exceptions raised while adding the firewall rules. """ current_rules = fe.FirewallRules(self.project_id, add_rule_callback=add_rule_callback) try: current_rules.add_rules_from_api(self.compute_client) except api_errors.ApiNotEnabledError as e: LOGGER.error('Error getting firewall rules for project %s: %s', self.project_id, e) raise ComputeApiDisabledError(e) except api_errors.ApiExecutionError as e: http_error = e.http_error if _is_project_deleted_error(http_error): LOGGER.warning('Project %s has been deleted.', self.project_id) raise ProjectDeletedError(str(http_error)) raise EnforcementError( STATUS_ERROR, 'error getting current firewall rules from API: %s' % http_error) except (fe.DuplicateFirewallRuleNameError, fe.InvalidFirewallRuleError) as e: raise EnforcementError( STATUS_ERROR, 'error getting current firewall ' 'rules from API: %s' % e) return current_rules
def enforce_firewall_policy(self, firewall_policy, current_rules=None, networks=None, allow_empty_ruleset=False, prechange_callback=None, add_rule_callback=None, retry_on_dry_run=False, maximum_retries=MAX_ENFORCEMENT_RETRIES): """Enforces the firewall policy on the project. Args: firewall_policy (list): A list of firewall rules that should be configured on the project networks. current_rules (str): A JSON str of firewall rules that specify current firewall policies. networks (list): A list of networks on the project that the policy applies to. If undefined, then the policy will be applied to all networks. allow_empty_ruleset (bool): If set to true and firewall_policy has no rules, all current firewall rules will be deleted from the project. prechange_callback (Callable): See FirewallEnforcer.apply_firewall() docstring for more details. add_rule_callback (Callable): A callback function that checks whether a firewall rule should be applied. If the callback returns False, that rule will not be modified. retry_on_dry_run (bool): Set to True to retry applying firewall rules when the expected policy does not match the current policy when dry_run is enabled. maximum_retries (int): The number of times enforce_firewall_policy will attempt to set the current firewall policy to the expected firewall policy. Set to 0 to disable retry behavior. Returns: enforcer_log_pb2.ProjectResult: A proto with details on the status of the enforcement and an audit log with any changes made. """ try: if networks: networks = sorted(networks) else: networks = self._get_project_networks() if not networks: self._set_error_status('no networks found for project') return self.result expected_rules = self._get_expected_rules(networks, firewall_policy) if not current_rules: rules_before_enforcement = self._get_current_fw_rules( add_rule_callback) else: rules_before_enforcement = fe.FirewallRules(self.project_id) rules_before_enforcement.add_rules_from_json(current_rules) except EnforcementError as e: self._set_error_status(e.reason()) except (ComputeApiDisabledError, ProjectDeletedError) as e: self._set_deleted_status(e) else: firewall_enforcer = self._initialize_firewall_enforcer( expected_rules, rules_before_enforcement, add_rule_callback) rules_after_enforcement = self._apply_firewall_policy( firewall_enforcer, expected_rules, networks, allow_empty_ruleset, prechange_callback, add_rule_callback, retry_on_dry_run, maximum_retries) if self.result.status == STATUS_UNSPECIFIED: self.result.status = STATUS_SUCCESS self._update_fw_results(firewall_enforcer, rules_before_enforcement, rules_after_enforcement) if not self.result.gce_firewall_enforcement.rules_modified_count: LOGGER.info('Firewall policy not changed for %s', self.project_id) return self.result