@Override public boolean applyFirewallRules( List<FirewallRuleVO> rules, boolean continueOnError, Account caller) { if (rules.size() == 0) { s_logger.debug("There are no firewall rules to apply"); return true; } for (FirewallRuleVO rule : rules) { // load cidrs if any rule.setSourceCidrList(_firewallCidrsDao.getSourceCidrs(rule.getId())); } if (caller != null) { _accountMgr.checkAccess(caller, null, true, rules.toArray(new FirewallRuleVO[rules.size()])); } try { if (!applyRules(rules, continueOnError, true)) { return false; } } catch (ResourceUnavailableException ex) { s_logger.warn("Failed to apply firewall rules due to ", ex); return false; } return true; }
@Override public boolean applyRules( List<? extends FirewallRule> rules, boolean continueOnError, boolean updateRulesInDB) throws ResourceUnavailableException { boolean success = true; if (rules == null || rules.size() == 0) { s_logger.debug("There are no rules to forward to the network elements"); return true; } Purpose purpose = rules.get(0).getPurpose(); if (!_ipAddrMgr.applyRules(rules, purpose, this, continueOnError)) { s_logger.warn("Rules are not completely applied"); return false; } else { if (updateRulesInDB) { for (FirewallRule rule : rules) { if (rule.getState() == FirewallRule.State.Revoke) { FirewallRuleVO relatedRule = _firewallDao.findByRelatedId(rule.getId()); if (relatedRule != null) { s_logger.warn( "Can't remove the firewall rule id=" + rule.getId() + " as it has related firewall rule id=" + relatedRule.getId() + "; leaving it in Revoke state"); success = false; } else { removeRule(rule); if (rule.getSourceIpAddressId() != null) { // if the rule is the last one for the ip address assigned to VPC, unassign it from // the network IpAddress ip = _ipAddressDao.findById(rule.getSourceIpAddressId()); _vpcMgr.unassignIPFromVpcNetwork(ip.getId(), rule.getNetworkId()); } } } else if (rule.getState() == FirewallRule.State.Add) { FirewallRuleVO ruleVO = _firewallDao.findById(rule.getId()); ruleVO.setState(FirewallRule.State.Active); _firewallDao.update(ruleVO.getId(), ruleVO); } } } } return success; }
@Override @DB public void revokeRule(FirewallRuleVO rule, Account caller, long userId, boolean needUsageEvent) { if (caller != null) { _accountMgr.checkAccess(caller, null, true, rule); } Transaction txn = Transaction.currentTxn(); boolean generateUsageEvent = false; txn.start(); if (rule.getState() == State.Staged) { if (s_logger.isDebugEnabled()) { s_logger.debug("Found a rule that is still in stage state so just removing it: " + rule); } removeRule(rule); generateUsageEvent = true; } else if (rule.getState() == State.Add || rule.getState() == State.Active) { rule.setState(State.Revoke); _firewallDao.update(rule.getId(), rule); generateUsageEvent = true; } if (generateUsageEvent && needUsageEvent) { UsageEventUtils.publishUsageEvent( EventTypes.EVENT_NET_RULE_DELETE, rule.getAccountId(), 0, rule.getId(), null, rule.getClass().getName(), rule.getUuid()); } txn.commit(); }
@Override @ActionEvent( eventType = EventTypes.EVENT_FIREWALL_CLOSE, eventDescription = "revoking firewall rule", async = true) public boolean revokeAllFirewallRulesForNetwork(long networkId, long userId, Account caller) throws ResourceUnavailableException { List<FirewallRule> rules = new ArrayList<FirewallRule>(); List<FirewallRuleVO> fwRules = _firewallDao.listByNetworkAndPurposeAndNotRevoked(networkId, Purpose.Firewall); if (s_logger.isDebugEnabled()) { s_logger.debug("Releasing " + fwRules.size() + " firewall rules for network id=" + networkId); } for (FirewallRuleVO rule : fwRules) { // Mark all Firewall rules as Revoke, but don't revoke them yet - we have to revoke all rules // for ip, no // need to send them one by one revokeFirewallRule(rule.getId(), false, caller, Account.ACCOUNT_ID_SYSTEM); } // now send everything to the backend List<FirewallRuleVO> rulesToApply = _firewallDao.listByNetworkAndPurpose(networkId, Purpose.Firewall); boolean success = applyFirewallRules(rulesToApply, true, caller); // Now we check again in case more rules have been inserted. rules.addAll(_firewallDao.listByNetworkAndPurposeAndNotRevoked(networkId, Purpose.Firewall)); if (s_logger.isDebugEnabled()) { s_logger.debug( "Successfully released firewall rules for network id=" + networkId + " and # of rules now = " + rules.size()); } return success && rules.size() == 0; }
@DB public boolean deleteLoadBalancerRule( long loadBalancerId, boolean apply, Account caller, long callerUserId) { LoadBalancerVO lb = _lbDao.findById(loadBalancerId); Transaction txn = Transaction.currentTxn(); boolean generateUsageEvent = false; boolean success = true; txn.start(); if (lb.getState() == FirewallRule.State.Staged) { if (s_logger.isDebugEnabled()) { s_logger.debug("Found a rule that is still in stage state so just removing it: " + lb); } generateUsageEvent = true; } else if (lb.getState() == FirewallRule.State.Add || lb.getState() == FirewallRule.State.Active) { lb.setState(FirewallRule.State.Revoke); _lbDao.persist(lb); generateUsageEvent = true; } List<LoadBalancerVMMapVO> maps = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId); if (maps != null) { for (LoadBalancerVMMapVO map : maps) { map.setRevoke(true); _lb2VmMapDao.persist(map); s_logger.debug( "Set load balancer rule for revoke: rule id " + loadBalancerId + ", vmId " + map.getInstanceId()); } } if (generateUsageEvent) { // Generate usage event right after all rules were marked for revoke UsageEventVO usageEvent = new UsageEventVO( EventTypes.EVENT_LOAD_BALANCER_DELETE, lb.getAccountId(), 0, lb.getId(), null); _usageEventDao.persist(usageEvent); } txn.commit(); if (apply) { try { if (!applyLoadBalancerConfig(loadBalancerId)) { s_logger.warn("Unable to apply the load balancer config"); return false; } } catch (ResourceUnavailableException e) { s_logger.warn( "Unable to apply the load balancer config because resource is unavaliable.", e); return false; } } FirewallRuleVO relatedRule = _firewallDao.findByRelatedId(lb.getId()); if (relatedRule != null) { s_logger.warn( "Unable to remove firewall rule id=" + lb.getId() + " as it has related firewall rule id=" + relatedRule.getId() + "; leaving it in Revoke state"); success = false; } else { _firewallDao.remove(lb.getId()); } _elbMgr.handleDeleteLoadBalancerRule(lb, callerUserId, caller); if (success) { s_logger.debug("Load balancer with id " + lb.getId() + " is removed successfully"); } return success; }
@Override public void detectRulesConflict(FirewallRule newRule) throws NetworkRuleConflictException { List<FirewallRuleVO> rules; if (newRule.getSourceIpAddressId() != null) { rules = _firewallDao.listByIpAndPurposeAndNotRevoked(newRule.getSourceIpAddressId(), null); assert (rules.size() >= 1) : "For network rules, we now always first persist the rule and then check for " + "network conflicts so we should at least have one rule at this point."; } else { // fetches only firewall egress rules. rules = _firewallDao.listByNetworkPurposeTrafficTypeAndNotRevoked( newRule.getNetworkId(), Purpose.Firewall, newRule.getTrafficType()); assert (rules.size() >= 1); } for (FirewallRuleVO rule : rules) { if (rule.getId() == newRule.getId()) { continue; // Skips my own rule. } boolean oneOfRulesIsFirewall = ((rule.getPurpose() == Purpose.Firewall || newRule.getPurpose() == Purpose.Firewall) && ((newRule.getPurpose() != rule.getPurpose()) || (!newRule.getProtocol().equalsIgnoreCase(rule.getProtocol())))); // if both rules are firewall and their cidrs are different, we can skip port ranges // verification boolean bothRulesFirewall = (rule.getPurpose() == newRule.getPurpose() && rule.getPurpose() == Purpose.Firewall); boolean duplicatedCidrs = false; if (bothRulesFirewall) { // Verify that the rules have different cidrs _firewallDao.loadSourceCidrs(rule); _firewallDao.loadSourceCidrs((FirewallRuleVO) newRule); List<String> ruleCidrList = rule.getSourceCidrList(); List<String> newRuleCidrList = newRule.getSourceCidrList(); if (ruleCidrList == null || newRuleCidrList == null) { continue; } Collection<String> similar = new HashSet<String>(ruleCidrList); similar.retainAll(newRuleCidrList); if (similar.size() > 0) { duplicatedCidrs = true; } } if (!oneOfRulesIsFirewall) { if (rule.getPurpose() == Purpose.StaticNat && newRule.getPurpose() != Purpose.StaticNat) { throw new NetworkRuleConflictException( "There is 1 to 1 Nat rule specified for the ip address id=" + newRule.getSourceIpAddressId()); } else if (rule.getPurpose() != Purpose.StaticNat && newRule.getPurpose() == Purpose.StaticNat) { throw new NetworkRuleConflictException( "There is already firewall rule specified for the ip address id=" + newRule.getSourceIpAddressId()); } } if (rule.getNetworkId() != newRule.getNetworkId() && rule.getState() != State.Revoke) { throw new NetworkRuleConflictException( "New rule is for a different network than what's specified in rule " + rule.getXid()); } if (newRule.getProtocol().equalsIgnoreCase(NetUtils.ICMP_PROTO) && newRule.getProtocol().equalsIgnoreCase(rule.getProtocol())) { if (newRule.getIcmpCode().longValue() == rule.getIcmpCode().longValue() && newRule.getIcmpType().longValue() == rule.getIcmpType().longValue() && newRule.getProtocol().equalsIgnoreCase(rule.getProtocol()) && duplicatedCidrs) { throw new InvalidParameterValueException( "New rule conflicts with existing rule id=" + rule.getId()); } } boolean notNullPorts = (newRule.getSourcePortStart() != null && newRule.getSourcePortEnd() != null && rule.getSourcePortStart() != null && rule.getSourcePortEnd() != null); if (!notNullPorts) { continue; } else if (!oneOfRulesIsFirewall && !(bothRulesFirewall && !duplicatedCidrs) && ((rule.getSourcePortStart().intValue() <= newRule.getSourcePortStart().intValue() && rule.getSourcePortEnd().intValue() >= newRule.getSourcePortStart().intValue()) || (rule.getSourcePortStart().intValue() <= newRule.getSourcePortEnd().intValue() && rule.getSourcePortEnd().intValue() >= newRule.getSourcePortEnd().intValue()) || (newRule.getSourcePortStart().intValue() <= rule.getSourcePortStart().intValue() && newRule.getSourcePortEnd().intValue() >= rule.getSourcePortStart().intValue()) || (newRule.getSourcePortStart().intValue() <= rule.getSourcePortEnd().intValue() && newRule.getSourcePortEnd().intValue() >= rule.getSourcePortEnd().intValue()))) { // we allow port forwarding rules with the same parameters but different protocols boolean allowPf = (rule.getPurpose() == Purpose.PortForwarding && newRule.getPurpose() == Purpose.PortForwarding && !newRule.getProtocol().equalsIgnoreCase(rule.getProtocol())); boolean allowStaticNat = (rule.getPurpose() == Purpose.StaticNat && newRule.getPurpose() == Purpose.StaticNat && !newRule.getProtocol().equalsIgnoreCase(rule.getProtocol())); if (!(allowPf || allowStaticNat || oneOfRulesIsFirewall)) { throw new NetworkRuleConflictException( "The range specified, " + newRule.getSourcePortStart() + "-" + newRule.getSourcePortEnd() + ", conflicts with rule " + rule.getId() + " which has " + rule.getSourcePortStart() + "-" + rule.getSourcePortEnd()); } } } if (s_logger.isDebugEnabled()) { s_logger.debug( "No network rule conflicts detected for " + newRule + " against " + (rules.size() - 1) + " existing rules"); } }
@DB protected FirewallRule createFirewallRule( Long ipAddrId, Account caller, String xId, Integer portStart, Integer portEnd, String protocol, List<String> sourceCidrList, Integer icmpCode, Integer icmpType, Long relatedRuleId, FirewallRule.FirewallRuleType type, Long networkId, FirewallRule.TrafficType trafficType) throws NetworkRuleConflictException { IPAddressVO ipAddress = null; if (ipAddrId != null) { // this for ingress firewall rule, for egress id is null ipAddress = _ipAddressDao.findById(ipAddrId); // Validate ip address if (ipAddress == null && type == FirewallRule.FirewallRuleType.User) { throw new InvalidParameterValueException( "Unable to create firewall rule; " + "couldn't locate IP address by id in the system"); } _networkModel.checkIpForService(ipAddress, Service.Firewall, null); } validateFirewallRule( caller, ipAddress, portStart, portEnd, protocol, Purpose.Firewall, type, networkId, trafficType); // icmp code and icmp type can't be passed in for any other protocol rather than icmp if (!protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO) && (icmpCode != null || icmpType != null)) { throw new InvalidParameterValueException( "Can specify icmpCode and icmpType for ICMP protocol only"); } if (protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO) && (portStart != null || portEnd != null)) { throw new InvalidParameterValueException( "Can't specify start/end port when protocol is ICMP"); } Long accountId = null; Long domainId = null; if (ipAddress != null) { // Ingress firewall rule accountId = ipAddress.getAllocatedToAccountId(); domainId = ipAddress.getAllocatedInDomainId(); } else if (networkId != null) { // egress firewall rule Network network = _networkModel.getNetwork(networkId); accountId = network.getAccountId(); domainId = network.getDomainId(); } Transaction txn = Transaction.currentTxn(); txn.start(); FirewallRuleVO newRule = new FirewallRuleVO( xId, ipAddrId, portStart, portEnd, protocol.toLowerCase(), networkId, accountId, domainId, Purpose.Firewall, sourceCidrList, icmpCode, icmpType, relatedRuleId, trafficType); newRule.setType(type); newRule = _firewallDao.persist(newRule); if (type == FirewallRuleType.User) detectRulesConflict(newRule); if (!_firewallDao.setStateToAdd(newRule)) { throw new CloudRuntimeException("Unable to update the state to add for " + newRule); } CallContext.current().setEventDetails("Rule Id: " + newRule.getId()); txn.commit(); return newRule; }
@Override @ActionEvent( eventType = EventTypes.EVENT_FIREWALL_CLOSE, eventDescription = "revoking firewall rule", async = true) public boolean revokeFirewallRulesForVm(long vmId) { boolean success = true; UserVmVO vm = _vmDao.findByIdIncludingRemoved(vmId); if (vm == null) { return false; } List<PortForwardingRuleVO> pfRules = _pfRulesDao.listByVm(vmId); List<FirewallRuleVO> staticNatRules = _firewallDao.listStaticNatByVmId(vm.getId()); List<FirewallRuleVO> firewallRules = new ArrayList<FirewallRuleVO>(); // Make a list of firewall rules to reprogram for (PortForwardingRuleVO pfRule : pfRules) { FirewallRuleVO relatedRule = _firewallDao.findByRelatedId(pfRule.getId()); if (relatedRule != null) { firewallRules.add(relatedRule); } } for (FirewallRuleVO staticNatRule : staticNatRules) { FirewallRuleVO relatedRule = _firewallDao.findByRelatedId(staticNatRule.getId()); if (relatedRule != null) { firewallRules.add(relatedRule); } } Set<Long> ipsToReprogram = new HashSet<Long>(); if (firewallRules.isEmpty()) { s_logger.debug("No firewall rules are found for vm id=" + vmId); return true; } else { s_logger.debug("Found " + firewallRules.size() + " to cleanup for vm id=" + vmId); } for (FirewallRuleVO rule : firewallRules) { // Mark firewall rules as Revoked, but don't revoke it yet (apply=false) revokeFirewallRule( rule.getId(), false, _accountMgr.getSystemAccount(), Account.ACCOUNT_ID_SYSTEM); ipsToReprogram.add(rule.getSourceIpAddressId()); } // apply rules for all ip addresses for (Long ipId : ipsToReprogram) { s_logger.debug( "Applying firewall rules for ip address id=" + ipId + " as a part of vm expunge"); try { success = success && applyIngressFirewallRules(ipId, _accountMgr.getSystemAccount()); } catch (ResourceUnavailableException ex) { s_logger.warn("Failed to apply port forwarding rules for ip id=" + ipId); success = false; } } return success; }