Support Firewall for public IPs in VPC (#12706)

This commit is contained in:
Harikrishna 2026-07-13 12:43:57 +05:30 committed by GitHub
parent f2a1f839a7
commit 13822b1ac1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
48 changed files with 1744 additions and 106 deletions

View File

@ -21,8 +21,13 @@ import java.util.List;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.rules.FirewallRule;
import com.cloud.network.vpc.Vpc;
public interface NetworkRuleApplier {
public boolean applyRules(Network network, FirewallRule.Purpose purpose, List<? extends FirewallRule> rules) throws ResourceUnavailableException;
default boolean applyRules(Network network, FirewallRule.Purpose purpose, List<? extends FirewallRule> rules) throws ResourceUnavailableException {
return applyRules(network, null, purpose, rules);
}
boolean applyRules(Network network, Vpc vpc, FirewallRule.Purpose purpose, List<? extends FirewallRule> rules) throws ResourceUnavailableException;
}

View File

@ -21,14 +21,20 @@ import java.util.List;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.Network;
import com.cloud.network.rules.FirewallRule;
import com.cloud.network.vpc.Vpc;
public interface FirewallServiceProvider extends NetworkElement {
/**
* Apply rules
* @param network
* @param rules
* @return
* @throws ResourceUnavailableException
* Apply firewall rules in a network context.
*/
boolean applyFWRules(Network network, List<? extends FirewallRule> rules) throws ResourceUnavailableException;
default boolean applyFWRules(Network network, List<? extends FirewallRule> rules) throws ResourceUnavailableException {
return false;
}
/**
* Apply firewall rules in a VPC context.
*/
default boolean applyFWRulesInVPC(Vpc vpc, List<? extends FirewallRule> rules) throws ResourceUnavailableException {
return false;
}
}

View File

@ -69,7 +69,9 @@ public interface FirewallRule extends ControlledEntity, Identity, InternalIdenti
State getState();
long getNetworkId();
Long getNetworkId();
Long getVpcId();
Long getSourceIpAddressId();

View File

@ -212,7 +212,7 @@ public class CreateEgressFirewallRuleCmd extends BaseAsyncCreateCmd implements F
}
@Override
public long getNetworkId() {
public Long getNetworkId() {
return networkId;
}

View File

@ -223,13 +223,9 @@ public class CreateFirewallRuleCmd extends BaseAsyncCreateCmd implements Firewal
}
@Override
public long getNetworkId() {
IpAddress ip = _entityMgr.findById(IpAddress.class, getIpAddressId());
Long ntwkId = null;
if (ip.getAssociatedWithNetworkId() != null) {
ntwkId = ip.getAssociatedWithNetworkId();
}
public Long getNetworkId() {
IpAddress ip = getIp();
Long ntwkId = isVpcIp(ip) ? getVpcNetworkIdForFirewallRule(ip) : getIsolatedNetworkIdForFirewallRule(ip);
if (ntwkId == null) {
throw new InvalidParameterValueException("Unable to create firewall rule for the IP address ID=" + ipAddressId +
@ -238,6 +234,12 @@ public class CreateFirewallRuleCmd extends BaseAsyncCreateCmd implements Firewal
return ntwkId;
}
@Override
public Long getVpcId() {
IpAddress ip = getIp();
return isVpcIp(ip) ? ip.getVpcId() : null;
}
@Override
public long getEntityOwnerId() {
Account account = CallContext.current().getCallingAccount();
@ -300,7 +302,21 @@ public class CreateFirewallRuleCmd extends BaseAsyncCreateCmd implements Firewal
@Override
public Long getSyncObjId() {
return getIp().getAssociatedWithNetworkId();
Long syncObjId = getIp().getAssociatedWithNetworkId();
return syncObjId != null ? syncObjId : getNetworkId();
}
private boolean isVpcIp(IpAddress ip) {
return ip.getVpcId() != null;
}
private Long getIsolatedNetworkIdForFirewallRule(IpAddress ip) {
return ip.getAssociatedWithNetworkId();
}
private Long getVpcNetworkIdForFirewallRule(IpAddress ip) {
// VPC flow is independent from tier association; manager resolves execution network.
return ip.getNetworkId();
}
private IpAddress getIp() {
@ -311,6 +327,7 @@ public class CreateFirewallRuleCmd extends BaseAsyncCreateCmd implements Firewal
return ip;
}
@Override
public Integer getIcmpCode() {
if (icmpCode != null) {

View File

@ -176,7 +176,7 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P
}
}
private Long getVpcId() {
public Long getVpcId() {
if (ipAddressId != null) {
IpAddress ipAddr = _networkService.getIp(ipAddressId);
if (ipAddr == null || !ipAddr.readyToUse()) {
@ -275,7 +275,7 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P
}
@Override
public long getNetworkId() {
public Long getNetworkId() {
IpAddress ip = _entityMgr.findById(IpAddress.class, getIpAddressId());
Long ntwkId = _networkService.getPreferredNetworkIdForPublicIpRuleAssignment(ip, networkId);
if (ntwkId == null) {

View File

@ -229,8 +229,13 @@ public class CreateIpForwardingRuleCmd extends BaseAsyncCreateCmd implements Sta
}
@Override
public long getNetworkId() {
return -1;
public Long getNetworkId() {
return -1L;
}
@Override
public Long getVpcId() {
return null;
}
@Override

View File

@ -51,6 +51,10 @@ public class FirewallResponse extends BaseResponse {
@Param(description = "The Network ID of the firewall rule")
private String networkId;
@SerializedName(ApiConstants.VPC_ID)
@Param(description = "The VPC ID of the firewall rule")
private String vpcId;
@SerializedName(ApiConstants.IP_ADDRESS)
@Param(description = "The public IP address for the firewall rule")
private String publicIpAddress;
@ -115,6 +119,10 @@ public class FirewallResponse extends BaseResponse {
this.networkId = networkId;
}
public void setVpcId(String vpcId) {
this.vpcId = vpcId;
}
public void setState(String state) {
this.state = state;
}

View File

@ -21,10 +21,15 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.cloud.network.IpAddress;
import com.cloud.network.NetworkService;
import com.cloud.utils.db.EntityManager;
import org.apache.commons.collections.CollectionUtils;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;
@ -33,6 +38,12 @@ import com.cloud.utils.net.NetUtils;
@RunWith(MockitoJUnitRunner.class)
public class CreateFirewallRuleCmdTest {
@Mock
private EntityManager entityManager;
@Mock
private NetworkService networkService;
private void validateAllIp4Cidr(final CreateFirewallRuleCmd cmd) {
Assert.assertTrue(CollectionUtils.isNotEmpty(cmd.getSourceCidrList()));
Assert.assertEquals(1, cmd.getSourceCidrList().size());
@ -88,4 +99,22 @@ public class CreateFirewallRuleCmdTest {
Assert.assertEquals(2, cmd.getSourceCidrList().size());
Assert.assertEquals(cidr, cmd.getSourceCidrList().get(1));
}
@Test
public void testGetNetworkIdVpcWithoutAssociatedNetworkUsesVpcFallbackAndSyncObjId() {
final CreateFirewallRuleCmd cmd = new CreateFirewallRuleCmd();
final IpAddress ip = Mockito.mock(IpAddress.class);
cmd._entityMgr = entityManager;
cmd._networkService = networkService;
ReflectionTestUtils.setField(cmd, "ipAddressId", 42L);
Mockito.when(networkService.getIp(42L)).thenReturn(ip);
Mockito.when(ip.getAssociatedWithNetworkId()).thenReturn(null);
Mockito.when(ip.getVpcId()).thenReturn(100L);
Mockito.when(ip.getNetworkId()).thenReturn(2L);
Assert.assertEquals(Long.valueOf(2L), cmd.getNetworkId());
Assert.assertEquals(Long.valueOf(2L), cmd.getSyncObjId());
}
}

View File

@ -37,6 +37,7 @@ public abstract class NetworkElementCommand extends Command {
public static final String ZONE_NETWORK_TYPE = "zone.network.type";
public static final String GUEST_BRIDGE = "guest.bridge";
public static final String VPC_PRIVATE_GATEWAY = "vpc.gateway.private";
public static final String VPC_ID = "vpc.id";
public static final String FIREWALL_EGRESS_DEFAULT = "firewall.egress.default";
public static final String NETWORK_PUB_LAST_IP = "network.public.last.ip";
public static final String HYPERVISOR_HOST_PRIVATE_IP = "hypervisor.private.ip";

View File

@ -32,6 +32,7 @@ import java.util.Set;
*/
public class SetFirewallRulesCommand extends NetworkElementCommand {
FirewallRuleTO[] rules;
Long vpcId;
protected SetFirewallRulesCommand() {
}
@ -40,10 +41,19 @@ public class SetFirewallRulesCommand extends NetworkElementCommand {
this.rules = rules.toArray(new FirewallRuleTO[rules.size()]);
}
public SetFirewallRulesCommand(List<FirewallRuleTO> rules, Long vpcId) {
this.rules = rules.toArray(new FirewallRuleTO[rules.size()]);
this.vpcId = vpcId;
}
public FirewallRuleTO[] getRules() {
return rules;
}
public Long getVpcId() {
return vpcId;
}
public String[][] generateFwRules() {
String[][] result = new String[2][];
Set<String> toAdd = new HashSet<String>();

View File

@ -80,10 +80,15 @@ public class StaticNatRuleImpl implements StaticNatRule {
}
@Override
public long getNetworkId() {
public Long getNetworkId() {
return networkId;
}
@Override
public Long getVpcId() {
return null;
}
@Override
public long getId() {
return id;

View File

@ -593,6 +593,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
defaultVPCOffProviders.put(Service.StaticNat, defaultProviders);
defaultVPCOffProviders.put(Service.PortForwarding, defaultProviders);
defaultVPCOffProviders.put(Service.Vpn, defaultProviders);
defaultVPCOffProviders.put(Service.Firewall, defaultProviders);
Transaction.execute(new TransactionCallbackNoReturn() {
@Override

View File

@ -69,6 +69,8 @@ public interface FirewallRulesDao extends GenericDao<FirewallRuleVO, Long> {
List<FirewallRuleVO> listByNetworkPurposeTrafficType(long networkId, FirewallRule.Purpose purpose, FirewallRule.TrafficType trafficType);
List<FirewallRuleVO> listByVpcPurposeTrafficType(long vpcId, FirewallRule.Purpose purpose, FirewallRule.TrafficType trafficType);
List<FirewallRuleVO> listByIpAndPurposeWithState(Long addressId, FirewallRule.Purpose purpose, FirewallRule.State state);
void loadSourceCidrs(FirewallRuleVO rule);

View File

@ -74,6 +74,7 @@ public class FirewallRulesDaoImpl extends GenericDaoBase<FirewallRuleVO, Long> i
AllFieldsSearch.and("domain", AllFieldsSearch.entity().getDomainId(), Op.EQ);
AllFieldsSearch.and("id", AllFieldsSearch.entity().getId(), Op.EQ);
AllFieldsSearch.and("networkId", AllFieldsSearch.entity().getNetworkId(), Op.EQ);
AllFieldsSearch.and("vpcId", AllFieldsSearch.entity().getVpcId(), Op.EQ);
AllFieldsSearch.and("related", AllFieldsSearch.entity().getRelated(), Op.EQ);
AllFieldsSearch.and("trafficType", AllFieldsSearch.entity().getTrafficType(), Op.EQ);
AllFieldsSearch.done();
@ -356,6 +357,22 @@ public class FirewallRulesDaoImpl extends GenericDaoBase<FirewallRuleVO, Long> i
return listBy(sc);
}
@Override
public List<FirewallRuleVO> listByVpcPurposeTrafficType(long vpcId, Purpose purpose, TrafficType trafficType) {
SearchCriteria<FirewallRuleVO> sc = AllFieldsSearch.create();
sc.setParameters("vpcId", vpcId);
if (purpose != null) {
sc.setParameters("purpose", purpose);
}
if (trafficType != null) {
sc.setParameters("trafficType", trafficType);
}
return listBy(sc);
}
@Override
@DB
public boolean remove(Long id) {

View File

@ -91,6 +91,9 @@ public class FirewallRuleVO implements FirewallRule {
@Column(name = "network_id")
Long networkId;
@Column(name = "vpc_id")
Long vpcId;
@Column(name = "icmp_code")
Integer icmpCode;
@ -196,10 +199,18 @@ public class FirewallRuleVO implements FirewallRule {
}
@Override
public long getNetworkId() {
public Long getNetworkId() {
return networkId;
}
public Long getVpcId() {
return vpcId;
}
public void setVpcId(Long vpcId) {
this.vpcId = vpcId;
}
@Override
public FirewallRuleType getType() {
return type;
@ -217,7 +228,7 @@ public class FirewallRuleVO implements FirewallRule {
uuid = UUID.randomUUID().toString();
}
public FirewallRuleVO(String xId, Long ipAddressId, Integer portStart, Integer portEnd, String protocol, long networkId, long accountId, long domainId,
public FirewallRuleVO(String xId, Long ipAddressId, Integer portStart, Integer portEnd, String protocol, Long networkId, long accountId, long domainId,
Purpose purpose, List<String> sourceCidrs, Integer icmpCode, Integer icmpType, Long related, TrafficType trafficType) {
this.xId = xId;
if (xId == null) {
@ -261,7 +272,7 @@ public class FirewallRuleVO implements FirewallRule {
}
public FirewallRuleVO(String xId, Long ipAddressId, Integer portStart, Integer portEnd, String protocol, long networkId, long accountId, long domainId,
public FirewallRuleVO(String xId, Long ipAddressId, Integer portStart, Integer portEnd, String protocol, Long networkId, long accountId, long domainId,
Purpose purpose, List<String> sourceCidrs, List<String> destCidrs, Integer icmpCode, Integer icmpType, Long related, TrafficType trafficType) {
this(xId,ipAddressId, portStart, portEnd, protocol, networkId, accountId, domainId, purpose, sourceCidrs, icmpCode, icmpType, related, trafficType);
this.destinationCidrs = destCidrs;

View File

@ -588,3 +588,6 @@ CREATE TABLE IF NOT EXISTS `cloud`.`dns_zone_network_map` (
CONSTRAINT `fk_dns_map__zone_id` FOREIGN KEY (`dns_zone_id`) REFERENCES `dns_zone` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_dns_map__network_id` FOREIGN KEY (`network_id`) REFERENCES `networks` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- This is part of allowing firewall rules on public IP addresses in VPC network
ALTER TABLE `cloud`.`firewall_rules` MODIFY COLUMN `network_id` BIGINT UNSIGNED NULL;

View File

@ -2986,9 +2986,8 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
defaultKubernetesServiceNetworkOfferingProviders.put(Service.UserData, provider);
if (forVpc) {
defaultKubernetesServiceNetworkOfferingProviders.put(Service.NetworkACL, forNsx ? Network.Provider.Nsx : provider);
} else {
defaultKubernetesServiceNetworkOfferingProviders.put(Service.Firewall, forNsx ? Network.Provider.Nsx : provider);
}
defaultKubernetesServiceNetworkOfferingProviders.put(Service.Firewall, forNsx ? Network.Provider.Nsx : provider);
defaultKubernetesServiceNetworkOfferingProviders.put(Service.Lb, forNsx ? Network.Provider.Nsx : provider);
defaultKubernetesServiceNetworkOfferingProviders.put(Service.SourceNat, forNsx ? Network.Provider.Nsx : provider);
defaultKubernetesServiceNetworkOfferingProviders.put(Service.StaticNat, forNsx ? Network.Provider.Nsx : provider);

View File

@ -155,7 +155,7 @@ public class KubernetesClusterManagerImplTest {
}
private FirewallRuleVO createRule(int startPort, int endPort) {
FirewallRuleVO rule = new FirewallRuleVO(null, null, startPort, endPort, "tcp", 1, 1, 1, FirewallRule.Purpose.Firewall, List.of("0.0.0.0/0"), null, null, null, FirewallRule.TrafficType.Ingress);
FirewallRuleVO rule = new FirewallRuleVO(null, null, startPort, endPort, "tcp", 1L, 1, 1, FirewallRule.Purpose.Firewall, List.of("0.0.0.0/0"), null, null, null, FirewallRule.TrafficType.Ingress);
return rule;
}

View File

@ -290,7 +290,7 @@ public class PaloAltoResourceTest {
List<FirewallRuleTO> rules = new ArrayList<FirewallRuleTO>();
List<String> cidrList = new ArrayList<String>();
cidrList.add("0.0.0.0/0");
FirewallRuleVO activeVO = new FirewallRuleVO(null, null, 80, 80, "tcp", 1, 1, 1, Purpose.Firewall, cidrList, null, null, null, FirewallRule.TrafficType.Egress);
FirewallRuleVO activeVO = new FirewallRuleVO(null, null, 80, 80, "tcp", 1L, 1, 1, Purpose.Firewall, cidrList, null, null, null, FirewallRule.TrafficType.Egress);
FirewallRuleTO active = new FirewallRuleTO(activeVO, Long.toString(vlanId), null, Purpose.Firewall, FirewallRule.TrafficType.Egress);
rules.add(active);
@ -319,7 +319,7 @@ public class PaloAltoResourceTest {
long vlanId = 3954;
List<FirewallRuleTO> rules = new ArrayList<FirewallRuleTO>();
FirewallRuleVO revokedVO = new FirewallRuleVO(null, null, 80, 80, "tcp", 1, 1, 1, Purpose.Firewall, null, null, null, null, FirewallRule.TrafficType.Egress);
FirewallRuleVO revokedVO = new FirewallRuleVO(null, null, 80, 80, "tcp", 1L, 1, 1, Purpose.Firewall, null, null, null, null, FirewallRule.TrafficType.Egress);
revokedVO.setState(State.Revoke);
FirewallRuleTO revoked = new FirewallRuleTO(revokedVO, Long.toString(vlanId), null, Purpose.Firewall, FirewallRule.TrafficType.Egress);
rules.add(revoked);

View File

@ -2968,8 +2968,21 @@ public class ApiResponseHelper implements ResponseGenerator, ResourceIdSupport {
}
}
Network network = ApiDBUtils.findNetworkById(fwRule.getNetworkId());
response.setNetworkId(network.getUuid());
Long networkId = fwRule.getNetworkId();
if (networkId != null) {
Network network = ApiDBUtils.findNetworkById(networkId);
if (network != null) {
response.setNetworkId(network.getUuid());
}
}
Long vpcId = fwRule.getVpcId();
if (vpcId != null) {
Vpc vpc = ApiDBUtils.findVpcById(vpcId);
if (vpc != null) {
response.setVpcId(vpc.getUuid());
}
}
FirewallRule.State state = fwRule.getState();
String stateToSet = state.toString();
@ -5420,8 +5433,21 @@ public class ApiResponseHelper implements ResponseGenerator, ResourceIdSupport {
response.setIcmpCode(fwRule.getIcmpCode());
response.setIcmpType(fwRule.getIcmpType());
Network network = ApiDBUtils.findNetworkById(fwRule.getNetworkId());
response.setNetworkId(network.getUuid());
Long networkId = fwRule.getNetworkId();
if (networkId != null) {
Network network = ApiDBUtils.findNetworkById(networkId);
if (network != null) {
response.setNetworkId(network.getUuid());
}
}
Long vpcId = fwRule.getVpcId();
if (vpcId != null) {
Vpc vpc = ApiDBUtils.findVpcById(vpcId);
if (vpc != null) {
response.setVpcId(vpc.getUuid());
}
}
FirewallRule.State state = fwRule.getState();
String stateToSet = state.toString();

View File

@ -7263,10 +7263,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
if (forVpc == null) {
if (service == Service.SecurityGroup || service == Service.Firewall) {
if (service == Service.SecurityGroup) {
forVpc = false;
} else if (service == Service.NetworkACL) {
forVpc = true;
} else if (service == Service.Firewall) {
forVpc = true;
}
}

View File

@ -659,28 +659,58 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
}
boolean success = true;
Network network = _networksDao.findById(rules.get(0).getNetworkId());
FirewallRuleVO.TrafficType trafficType = rules.get(0).getTrafficType();
FirewallRule firstRule = rules.get(0);
Long networkId = firstRule.getNetworkId();
Long vpcId = firstRule.getVpcId();
FirewallRuleVO.TrafficType trafficType = firstRule.getTrafficType();
List<PublicIp> publicIps = new ArrayList<PublicIp>();
if (!(rules.get(0).getPurpose() == FirewallRule.Purpose.Firewall && trafficType == FirewallRule.TrafficType.Egress)) {
// get the list of public ip's owned by the network
List<IPAddressVO> userIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), null);
if (userIps != null && !userIps.isEmpty()) {
for (IPAddressVO userIp : userIps) {
PublicIp publicIp = PublicIp.createFromAddrAndVlan(userIp, _vlanDao.findById(userIp.getVlanId()));
publicIps.add(publicIp);
// For VPC firewall rules the networkId on the rule is null; resolve via VPC.
Network network = null;
Vpc vpc = null;
if (networkId != null) {
network = _networksDao.findById(networkId);
} else if (vpcId != null) {
vpc = _vpcDao.findById(vpcId);
}
if (network == null) {
logger.warn("Unable to resolve network for firewall rules (networkId={}, vpcId={}); skipping IP association", networkId, vpcId);
} else if (!(firstRule.getPurpose() == FirewallRule.Purpose.Firewall && trafficType == FirewallRule.TrafficType.Egress)) {
// For VPC ingress rules, collect public IPs tied to the VPC rather than network association
if (vpcId != null && networkId == null) {
List<IPAddressVO> vpcIps = _ipAddressDao.listByAssociatedVpc(vpcId, null);
if (vpcIps != null) {
for (IPAddressVO userIp : vpcIps) {
PublicIp publicIp = PublicIp.createFromAddrAndVlan(userIp, _vlanDao.findById(userIp.getVlanId()));
publicIps.add(publicIp);
}
}
} else {
// get the list of public ip's owned by the network
List<IPAddressVO> userIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), null);
if (userIps != null && !userIps.isEmpty()) {
for (IPAddressVO userIp : userIps) {
PublicIp publicIp = PublicIp.createFromAddrAndVlan(userIp, _vlanDao.findById(userIp.getVlanId()));
publicIps.add(publicIp);
}
}
}
}
// rules can not programmed unless IP is associated with network service provider, so run IP assoication for
// rules can not programmed unless IP is associated with network service provider, so run IP association for
// the network so as to ensure IP is associated before applying rules (in add state)
if (checkIfIpAssocRequired(network, false, publicIps)) {
if (network != null && checkIfIpAssocRequired(network, false, publicIps)) {
applyIpAssociations(network, false, continueOnError, publicIps);
}
try {
applier.applyRules(network, purpose, rules);
if (network != null || vpc != null) {
applier.applyRules(network, vpc, purpose, rules);
} else {
logger.warn("Skipping applyRules: no network or vpc resolved for rules (networkId={}, vpcId={})", networkId, vpcId);
success = false;
}
} catch (ResourceUnavailableException e) {
if (!continueOnError) {
throw e;
@ -691,7 +721,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
// if there are no active rules associated with a public IP, then public IP need not be associated with a provider.
// This IPAssoc ensures, public IP is dis-associated after last active rule is revoked.
if (checkIfIpAssocRequired(network, true, publicIps)) {
if (network != null && checkIfIpAssocRequired(network, true, publicIps)) {
applyIpAssociations(network, true, continueOnError, publicIps);
}

View File

@ -107,9 +107,11 @@ import com.cloud.network.rules.FirewallRuleVO;
import com.cloud.network.rules.dao.PortForwardingRulesDao;
import com.cloud.network.vpc.Vpc;
import com.cloud.network.vpc.VpcGatewayVO;
import com.cloud.network.vpc.VpcOfferingServiceMapVO;
import com.cloud.network.vpc.dao.PrivateIpDao;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.network.vpc.dao.VpcGatewayDao;
import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
import com.cloud.offering.NetworkOffering;
import com.cloud.offering.NetworkOffering.Detail;
import com.cloud.offerings.NetworkOfferingServiceMapVO;
@ -186,6 +188,8 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
NetworkPermissionDao _networkPermissionDao;
@Inject
VpcDao vpcDao;
@Inject
VpcOfferingServiceMapDao _vpcOffSvcMapDao;
private List<NetworkElement> networkElements;
@ -510,12 +514,16 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
// We only support one provider for one service now
Map<Service, Set<Provider>> serviceToProviders = getServiceProvidersMap(networkId);
// Since IP already has service to bind with, the oldProvider can't be null
Set<Provider> newProviders = serviceToProviders.get(service);
Set<Provider> newProviders = getProvidersForServiceWithVpcFallback(serviceToProviders, service, publicIp.getVpcId());
if (newProviders == null || newProviders.isEmpty()) {
throw new InvalidParameterValueException("There is no new provider for IP " + publicIp.getAddress() + " of service " + service.getName() + "!");
}
Provider newProvider = (Provider)newProviders.toArray()[0];
Set<Provider> oldProviders = serviceToProviders.get(services.toArray()[0]);
Service existingService = (Service) services.toArray()[0];
Set<Provider> oldProviders = getProvidersForServiceWithVpcFallback(serviceToProviders, existingService, publicIp.getVpcId());
if (oldProviders == null || oldProviders.isEmpty()) {
throw new InvalidParameterValueException("There is no existing provider for IP " + publicIp.getAddress() + " of service " + existingService.getName() + "!");
}
Provider oldProvider = (Provider)oldProviders.toArray()[0];
Network network = _networksDao.findById(networkId);
NetworkElement oldElement = getElementImplementingProvider(oldProvider.getName());
@ -530,6 +538,35 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
return true;
}
private Set<Provider> getProvidersForServiceWithVpcFallback(Map<Service, Set<Provider>> serviceToProviders, Service service, Long vpcId) {
Set<Provider> providers = serviceToProviders.get(service);
if (providers != null && !providers.isEmpty()) {
return providers;
}
if (vpcId == null || service != Service.Firewall) {
return providers;
}
Set<Provider> vpcProviders = new HashSet<Provider>();
Vpc vpc = vpcDao.findById(vpcId);
if (vpc == null) {
return vpcProviders;
}
List<VpcOfferingServiceMapVO> offeringProviders = _vpcOffSvcMapDao.listProvidersForServiceForVpcOffering(vpc.getVpcOfferingId(), Service.Firewall);
if (offeringProviders != null) {
for (VpcOfferingServiceMapVO offeringProvider : offeringProviders) {
Provider provider = Provider.getProvider(offeringProvider.getProvider());
if (provider != null) {
vpcProviders.add(provider);
}
}
}
return vpcProviders;
}
Map<Provider, Set<Service>> getProviderServicesMap(long networkId) {
Map<Provider, Set<Service>> map = new HashMap<Provider, Set<Service>>();
List<NetworkServiceMapVO> nsms = _ntwkSrvcDao.getServicesInNetwork(networkId);

View File

@ -210,6 +210,10 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ
return true;
}
protected boolean canHandle(final Vpc vpc, final Service service) {
return false;
}
@Override
public boolean implement(final Network network, final NetworkOffering offering, final DeployDestination dest, final ReservationContext context)
throws ResourceUnavailableException, ConcurrentOperationException, InsufficientCapacityException {
@ -279,7 +283,7 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ
if (canHandle(network, Service.Firewall)) {
final List<DomainRouterVO> routers = getRouters(network);
if (routers == null || routers.isEmpty()) {
logger.debug("Virtual router element doesn't need to apply firewall rules on the backend; virtual router doesn't exist in the network {}", network);
logger.debug("Virtual router element doesn't need to apply firewall rules on the backend; virtual router doesn't exist in the network {}");
return true;
}
@ -302,6 +306,7 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ
return result;
}
@Override
public boolean validateLBRule(final Network network, final LoadBalancingRule rule) {
final List<LoadBalancingRule> rules = new ArrayList<LoadBalancingRule>();

View File

@ -52,6 +52,7 @@ import com.cloud.network.router.VirtualRouter;
import com.cloud.network.router.VirtualRouter.Role;
import com.cloud.network.router.VpcNetworkHelperImpl;
import com.cloud.network.router.VpcVirtualNetworkApplianceManager;
import com.cloud.network.rules.FirewallRule;
import com.cloud.network.vpc.NetworkACLItem;
import com.cloud.network.vpc.NetworkACLItemDao;
import com.cloud.network.vpc.NetworkACLItemVO;
@ -148,6 +149,49 @@ public class VpcVirtualRouterElement extends VirtualRouterElement implements Vpc
return true;
}
@Override
protected boolean canHandle(final Vpc vpc, final Service service) {
if (vpc == null) {
return false;
}
if (!_networkMdl.isProviderEnabledInZone(vpc.getZoneId(), Network.Provider.VPCVirtualRouter.getName())) {
return false;
}
if (service != null && !_vpcMgr.isProviderSupportServiceInVpc(vpc.getId(), service, getProvider())) {
logger.trace("Element " + getProvider().getName() + " doesn't support service " + service.getName() + " in the vpc " + vpc);
return false;
}
return true;
}
@Override
public boolean applyFWRulesInVPC(final Vpc vpc, final List<? extends FirewallRule> rules) throws ResourceUnavailableException {
boolean result = true;
if (canHandle(vpc, Service.Firewall)) {
final List<DomainRouterVO> routers = _routerDao.listByVpcId(vpc.getId());
if (CollectionUtils.isEmpty(routers)) {
logger.debug("Virtual router element doesn't need to apply firewall rules on the backend; virtual router doesn't exist in the vpc");
return true;
}
Network network = null;
if (CollectionUtils.isNotEmpty(rules) && rules.get(0).getNetworkId() != null) {
network = _networkModel.getNetwork(rules.get(0).getNetworkId());
}
final DataCenterVO dcVO = _dcDao.findById(vpc.getZoneId());
final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO);
for (final DomainRouterVO domainRouterVO : routers) {
result = result && networkTopology.applyFirewallRulesInVPC(vpc, rules, domainRouterVO);
}
}
return result;
}
@Override
public boolean implementVpc(final Vpc vpc, final DeployDestination dest, final ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException,
InsufficientCapacityException {
@ -412,10 +456,6 @@ public class VpcVirtualRouterElement extends VirtualRouterElement implements Vpc
vpnCapabilities.putAll(capabilities.get(Service.Vpn));
vpnCapabilities.put(Capability.VpnTypes, "s2svpn");
capabilities.put(Service.Vpn, vpnCapabilities);
// remove firewall capability
capabilities.remove(Service.Firewall);
// add network ACL capability
final Map<Capability, String> networkACLCapabilities = new HashMap<Capability, String>();
networkACLCapabilities.put(Capability.SupportedProtocols, "tcp,udp,icmp");

View File

@ -203,25 +203,67 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
if (sourceCidrs != null && !sourceCidrs.isEmpty())
Collections.replaceAll(sourceCidrs, "0.0.0.0/0", network.getCidr());
return createFirewallRule(null, caller, rule.getXid(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol(), sourceCidrs, rule.getDestinationCidrList(),
rule.getIcmpCode(), rule.getIcmpType(), null, rule.getType(), rule.getNetworkId(), rule.getTrafficType(), rule.isDisplay());
return createFirewallRuleForNonVPC(null, caller, rule.getXid(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol(), sourceCidrs,
rule.getDestinationCidrList(), rule.getIcmpCode(), rule.getIcmpType(), null, rule.getType(), rule.getNetworkId(), rule.getTrafficType(), rule.isDisplay());
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_FIREWALL_OPEN, eventDescription = "creating firewall rule", create = true)
public FirewallRule createIngressFirewallRule(FirewallRule rule) throws NetworkRuleConflictException {
Account caller = CallContext.current().getCallingAccount();
Account caller = CallContext.current().getCallingAccount();
Long sourceIpAddressId = rule.getSourceIpAddressId();
IPAddressVO sourceIp = getSourceIpForIngressRule(sourceIpAddressId);
return createFirewallRule(sourceIpAddressId, caller, rule.getXid(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol(),
rule.getSourceCidrList(), null, rule.getIcmpCode(), rule.getIcmpType(), null, rule.getType(), rule.getNetworkId(), rule.getTrafficType(), rule.isDisplay());
if (sourceIp.getVpcId() != null) {
return createIngressFirewallRuleForVpcIp(rule, caller, sourceIp);
}
return createIngressFirewallRuleForIsolatedIp(rule, caller, sourceIp);
}
protected IPAddressVO getSourceIpForIngressRule(Long sourceIpAddressId) {
if (sourceIpAddressId == null) {
return null;
}
IPAddressVO sourceIp = _ipAddressDao.findById(sourceIpAddressId);
if (sourceIp == null) {
throw new CloudRuntimeException("Unable to find IP address by id=" + sourceIpAddressId);
}
return sourceIp;
}
protected FirewallRule createIngressFirewallRuleForIsolatedIp(FirewallRule rule, Account caller, IPAddressVO sourceIp)
throws NetworkRuleConflictException {
return createFirewallRuleForNonVPC(rule.getSourceIpAddressId(), caller, rule.getXid(), rule.getSourcePortStart(), rule.getSourcePortEnd(),
rule.getProtocol(), rule.getSourceCidrList(), null, rule.getIcmpCode(), rule.getIcmpType(), null, rule.getType(),
rule.getNetworkId(), rule.getTrafficType(), rule.isDisplay());
}
protected FirewallRule createIngressFirewallRuleForVpcIp(FirewallRule rule, Account caller, IPAddressVO sourceIp)
throws NetworkRuleConflictException {
Long vpcId = sourceIp != null ? sourceIp.getVpcId() : null;
return createFirewallRuleForVpc(rule.getSourceIpAddressId(), caller, rule.getXid(), rule.getSourcePortStart(), rule.getSourcePortEnd(),
rule.getProtocol(), rule.getSourceCidrList(), null, rule.getIcmpCode(), rule.getIcmpType(), null, rule.getType(),
vpcId, rule.getTrafficType(), rule.isDisplay());
}
//Destination CIDR capability is currently implemented for egress rules only. For others, the field is passed as null.
@DB
protected FirewallRule createFirewallRule(final Long ipAddrId, Account caller, final String xId, final Integer portStart, final Integer portEnd, final String protocol,
final List<String> sourceCidrList, final List<String> destCidrList, final Integer icmpCode, final Integer icmpType, final Long relatedRuleId,
final FirewallRule.FirewallRuleType type, final Long networkId, final FirewallRule.TrafficType trafficType, final Boolean forDisplay) throws NetworkRuleConflictException {
final FirewallRule.FirewallRuleType type, final Long networkId, final Long vpcId, final FirewallRule.TrafficType trafficType, final Boolean forDisplay) throws NetworkRuleConflictException {
if (vpcId != null) {
return createFirewallRuleForVpc(ipAddrId, caller, xId, portStart, portEnd, protocol, sourceCidrList, destCidrList, icmpCode, icmpType, relatedRuleId,
type, vpcId, trafficType, forDisplay);
}
return createFirewallRuleForNonVPC(ipAddrId, caller, xId, portStart, portEnd, protocol, sourceCidrList, destCidrList, icmpCode, icmpType, relatedRuleId,
type, networkId, trafficType, forDisplay);
}
@DB
protected FirewallRule createFirewallRuleForNonVPC(final Long ipAddrId, Account caller, final String xId, final Integer portStart, final Integer portEnd, final String protocol,
final List<String> sourceCidrList, final List<String> destCidrList, final Integer icmpCode, final Integer icmpType, final Long relatedRuleId,
final FirewallRule.FirewallRuleType type, final Long networkId, final FirewallRule.TrafficType trafficType, final Boolean forDisplay) throws NetworkRuleConflictException {
IPAddressVO ipAddress = null;
try {
// Validate ip address
@ -288,6 +330,161 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
}
}
@DB
protected FirewallRule createFirewallRuleForVpc(final Long ipAddrId, Account caller, final String xId, final Integer portStart, final Integer portEnd, final String protocol,
final List<String> sourceCidrList, final List<String> destCidrList, final Integer icmpCode, final Integer icmpType,
final Long relatedRuleId, final FirewallRuleType type, final Long vpcId,
final FirewallRule.TrafficType trafficType, final Boolean forDisplay) throws NetworkRuleConflictException {
IPAddressVO ipAddress = null;
try {
Long resolvedVpcId = vpcId;
if (ipAddrId != null) {
ipAddress = _ipAddressDao.acquireInLockTable(ipAddrId);
if (ipAddress == null) {
throw new InvalidParameterValueException("Unable to create firewall rule; " + "couldn't locate IP address by id in the system");
}
resolvedVpcId = resolvedVpcId != null ? resolvedVpcId : ipAddress.getVpcId();
}
if (resolvedVpcId == null) {
throw new InvalidParameterValueException("Unable to create VPC firewall rule; couldn't locate VPC id");
}
validateFirewallRuleForVpc(caller, ipAddress, portStart, portEnd, protocol, Purpose.Firewall, type, resolvedVpcId, trafficType);
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) {
accountId = ipAddress.getAllocatedToAccountId();
domainId = ipAddress.getAllocatedInDomainId();
} else {
Vpc vpc = _vpcMgr.getActiveVpc(resolvedVpcId);
if (vpc == null) {
throw new InvalidParameterValueException("Unable to create VPC firewall rule; couldn't locate VPC by id=" + resolvedVpcId);
}
accountId = vpc.getAccountId();
domainId = vpc.getDomainId();
}
final Long accountIdFinal = accountId;
final Long domainIdFinal = domainId;
final Long resolvedNetworkIdFinal = null;
final Long resolvedVpcIdFinal = resolvedVpcId;
return Transaction.execute((TransactionCallbackWithException<FirewallRuleVO, NetworkRuleConflictException>) status -> {
FirewallRuleVO newRule = new FirewallRuleVO(xId, ipAddrId, portStart, portEnd, protocol.toLowerCase(), resolvedNetworkIdFinal, accountIdFinal, domainIdFinal, Purpose.Firewall,
sourceCidrList, destCidrList, icmpCode, icmpType, relatedRuleId, trafficType);
newRule.setVpcId(resolvedVpcIdFinal);
newRule.setType(type);
if (forDisplay != null) {
newRule.setDisplay(forDisplay);
}
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.getUuid());
CallContext.current().putContextParameter(FirewallRule.class, newRule.getId());
return newRule;
});
} finally {
if (ipAddrId != null) {
_ipAddressDao.releaseFromLockTable(ipAddrId);
}
}
}
protected void validateFirewallRuleForVpc(Account caller, IPAddressVO ipAddress, Integer portStart, Integer portEnd, String proto, Purpose purpose,
FirewallRuleType type, Long vpcId, FirewallRule.TrafficType trafficType) {
if (portStart != null && !NetUtils.isValidPort(portStart)) {
throw new InvalidParameterValueException("publicPort is an invalid value: " + portStart);
}
if (portEnd != null && !NetUtils.isValidPort(portEnd)) {
throw new InvalidParameterValueException("Public port range is an invalid value: " + portEnd);
}
if (portStart != null && portEnd != null && portStart > portEnd) {
throw new InvalidParameterValueException("Start port can't be bigger than end port");
}
if (ipAddress == null && type == FirewallRuleType.System) {
return;
}
if (vpcId == null) {
throw new InvalidParameterValueException("Unable to retrieve VPC id to validate the rule");
}
if (ipAddress != null) {
_accountMgr.checkAccess(caller, null, true, ipAddress);
}
Vpc vpc = _vpcMgr.getActiveVpc(vpcId);
if (vpc == null) {
throw new InvalidParameterValueException("Unable to retrieve VPC to validate the rule by id=" + vpcId);
}
Map<Network.Capability, String> caps = null;
if (purpose == Purpose.Firewall) {
caps = getFirewallServiceCapabilitiesForVpc(vpcId);
if (caps == null) {
throw new InvalidParameterValueException("Firewall service is not supported in VPC " + vpc);
}
}
if (caps != null) {
String supportedTrafficTypes = null;
if (purpose == FirewallRule.Purpose.Firewall) {
supportedTrafficTypes = caps.get(Capability.SupportedTrafficDirection).toLowerCase();
}
String supportedProtocols;
if (purpose == FirewallRule.Purpose.Firewall && trafficType == FirewallRule.TrafficType.Egress) {
supportedProtocols = caps.get(Capability.SupportedEgressProtocols).toLowerCase();
} else {
supportedProtocols = caps.get(Capability.SupportedProtocols).toLowerCase();
}
if (!supportedProtocols.contains(proto.toLowerCase())) {
throw new InvalidParameterValueException("Protocol " + proto + " is not supported in VPC " + vpcId);
} else if (proto.equalsIgnoreCase(NetUtils.ICMP_PROTO) && purpose != Purpose.Firewall) {
throw new InvalidParameterValueException("Protocol " + proto + " is currently supported only for rules with purpose " + Purpose.Firewall);
} else if (purpose == Purpose.Firewall && !supportedTrafficTypes.contains(trafficType.toString().toLowerCase())) {
throw new InvalidParameterValueException(String.format("Traffic Type %s is currently supported by Firewall in VPC %s", trafficType, vpc.getUuid()));
}
}
}
protected Map<Network.Capability, String> getFirewallServiceCapabilitiesForVpc(Long vpcId) {
for (FirewallServiceProvider fwElement : _firewallElements) {
Network.Provider provider = fwElement.getProvider();
if (_vpcMgr.isProviderSupportServiceInVpc(vpcId, Service.Firewall, provider)) {
Map<Service, Map<Capability, String>> capabilities = fwElement.getCapabilities();
if (capabilities != null && capabilities.get(Service.Firewall) != null) {
return capabilities.get(Service.Firewall);
}
}
}
return null;
}
protected Long resolveIsolatedFirewallRuleNetworkId(IPAddressVO ipAddress, Long networkId) {
_networkModel.checkIpForService(ipAddress, Service.Firewall, networkId);
return ipAddress.getAssociatedWithNetworkId();
}
@Override
public Pair<List<? extends FirewallRule>, Integer> listFirewallRules(IListFirewallRulesCmd cmd) {
Long ipId = cmd.getIpAddressId();
@ -404,9 +601,16 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
assert (rules.size() >= 1);
}
NetworkVO newRuleNetwork = getNewRuleNetwork(newRule);
boolean newRuleIsOnVpcNetwork = newRuleNetwork.getVpcId() != null;
boolean vpcConserveModeEnabled = _vpcMgr.isNetworkOnVpcEnabledConserveMode(newRuleNetwork);
Long newRuleVpcId = newRule.getVpcId();
boolean newRuleIsVpc = newRuleVpcId != null;
NetworkVO newRuleNetwork = null;
boolean newRuleIsOnVpcNetwork = false;
boolean vpcConserveModeEnabled = false;
if (!newRuleIsVpc) {
newRuleNetwork = getNewRuleNetwork(newRule);
newRuleIsOnVpcNetwork = newRuleNetwork.getVpcId() != null;
vpcConserveModeEnabled = newRuleIsOnVpcNetwork && _vpcMgr.isNetworkOnVpcEnabledConserveMode(newRuleNetwork);
}
for (FirewallRuleVO rule : rules) {
if (rule.getId() == newRule.getId()) {
@ -457,8 +661,8 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
// Checking if the rule applied is to the same network that is passed in the rule.
// (except for VPCs with conserve mode = true)
if ((!newRuleIsOnVpcNetwork || !vpcConserveModeEnabled)
&& rule.getNetworkId() != newRule.getNetworkId() && rule.getState() != State.Revoke) {
if (!newRuleIsVpc && (!newRuleIsOnVpcNetwork || !vpcConserveModeEnabled)
&& !Objects.equals(rule.getNetworkId(), newRule.getNetworkId()) && rule.getState() != State.Revoke) {
String errMsg = String.format("New rule is for a different network than what's specified in rule %s", rule.getXid());
if (newRuleIsOnVpcNetwork) {
Vpc vpc = _vpcMgr.getActiveVpc(newRuleNetwork.getVpcId());
@ -580,11 +784,9 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
}
if (ipAddress != null) {
if (ipAddress.getAssociatedWithNetworkId() == null) {
throw new InvalidParameterValueException("Unable to create firewall rule ; ip with specified id is not associated with any network");
} else {
networkId = ipAddress.getAssociatedWithNetworkId();
}
networkId = isVpcIpAddress(ipAddress)
? validateFirewallRuleForVpcIp(ipAddress, networkId)
: validateFirewallRuleForIsolatedIp(ipAddress);
// Validate ip address
_accountMgr.checkAccess(caller, null, true, ipAddress);
@ -615,7 +817,7 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
if (routedIpv4Manager.isVirtualRouterGateway(network)) {
throw new CloudRuntimeException("Unable to create routing firewall rule. Please use routing firewall API instead.");
}
caps = _networkModel.getNetworkServiceCapabilities(network.getId(), Service.Firewall);
caps = getFirewallServiceCapabilities(network);
}
if (caps != null) {
@ -655,6 +857,41 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
}
protected boolean isVpcIpAddress(IPAddressVO ipAddress) {
return ipAddress.getVpcId() != null;
}
protected Long validateFirewallRuleForIsolatedIp(IPAddressVO ipAddress) {
if (ipAddress.getAssociatedWithNetworkId() == null) {
throw new InvalidParameterValueException("Unable to create firewall rule ; ip with specified id is not associated with any network");
}
return ipAddress.getAssociatedWithNetworkId();
}
protected Long validateFirewallRuleForVpcIp(IPAddressVO ipAddress, Long networkId) {
if (networkId == null) {
throw new InvalidParameterValueException("Unable to retrieve network id to validate the rule");
}
return networkId;
}
protected Map<Network.Capability, String> getFirewallServiceCapabilities(Network network) {
if (network.getVpcId() == null) {
return _networkModel.getNetworkServiceCapabilities(network.getId(), Service.Firewall);
}
for (FirewallServiceProvider fwElement : _firewallElements) {
Network.Provider provider = fwElement.getProvider();
if (_vpcMgr.isProviderSupportServiceInVpc(network.getVpcId(), Service.Firewall, provider)) {
Map<Service, Map<Capability, String>> capabilities = fwElement.getCapabilities();
if (capabilities != null && capabilities.get(Service.Firewall) != null) {
return capabilities.get(Service.Firewall);
}
}
}
return _networkModel.getNetworkServiceCapabilities(network.getId(), Service.Firewall);
}
@Override
public boolean applyRules(List<? extends FirewallRule> rules, boolean continueOnError, boolean updateRulesInDB) throws ResourceUnavailableException {
boolean success = true;
@ -683,7 +920,7 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
success = false;
} else {
removeRule(rule);
if (rule.getSourceIpAddressId() != null) {
if (rule.getSourceIpAddressId() != null && rule.getVpcId() == null) {
//if the rule is the last one for the ip address assigned to VPC, unassign it from the network
_vpcMgr.unassignIPFromVpcNetwork(rule.getSourceIpAddressId(), rule.getNetworkId());
}
@ -701,7 +938,7 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
}
@Override
public boolean applyRules(Network network, Purpose purpose, List<? extends FirewallRule> rules) throws ResourceUnavailableException {
public boolean applyRules(Network network, Vpc vpc, Purpose purpose, List<? extends FirewallRule> rules) throws ResourceUnavailableException {
boolean handled = false;
switch (purpose) {
/* StaticNatRule would be applied by Firewall provider, since the incompatible of two object */
@ -710,11 +947,26 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
case Ipv6Firewall:
for (FirewallServiceProvider fwElement : _firewallElements) {
Network.Provider provider = fwElement.getProvider();
boolean isFwProvider = _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Firewall, provider);
boolean isFwProvider;
Long effectiveVpcId = null;
if (vpc != null) {
effectiveVpcId = vpc.getId();
} else if (network != null) {
effectiveVpcId = network.getVpcId();
}
if (effectiveVpcId != null) {
isFwProvider = _vpcMgr.isProviderSupportServiceInVpc(effectiveVpcId, Service.Firewall, provider);
} else {
isFwProvider = _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Firewall, provider);
}
if (!isFwProvider) {
continue;
}
handled = fwElement.applyFWRules(network, rules);
if (vpc != null) {
handled = fwElement.applyFWRulesInVPC(vpc, rules);
} else {
handled = fwElement.applyFWRules(network, rules);
}
if (handled)
break;
}
@ -771,6 +1023,11 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
return handled;
}
@Override
public boolean applyRules(Network network, Purpose purpose, List<? extends FirewallRule> rules) throws ResourceUnavailableException {
return applyRules(network, null, purpose, rules);
}
@Override
public void removeRule(FirewallRule rule) {
@ -817,8 +1074,10 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
for (FirewallRuleVO rule : rules) {
// validate rule - for NSX
long networkId = rule.getNetworkId();
validateNsxConstraints(networkId, rule);
Long networkId = rule.getNetworkId();
if (networkId != null) {
validateNsxConstraints(networkId, rule);
}
// load cidrs if any
rule.setSourceCidrList(_firewallCidrsDao.getSourceCidrs(rule.getId()));
rule.setDestinationCidrsList(_firewallDcidrsDao.getDestCidrs(rule.getId()));
@ -1078,7 +1337,7 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
List<String> oneCidr = new ArrayList<String>();
oneCidr.add(NetUtils.ALL_IP4_CIDRS);
return createFirewallRule(ipAddrId, caller, null, startPort, endPort, protocol, oneCidr, null, icmpCode, icmpType, relatedRuleId, FirewallRule.FirewallRuleType.User,
networkId, FirewallRule.TrafficType.Ingress, true);
networkId, null, FirewallRule.TrafficType.Ingress, true);
}
@Override
@ -1193,7 +1452,7 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
_firewallDao.loadSourceCidrs(rule);
}
createFirewallRule(ip.getId(), acct, rule.getXid(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol(), rule.getSourceCidrList(),null,
rule.getIcmpCode(), rule.getIcmpType(), rule.getRelated(), FirewallRuleType.System, rule.getNetworkId(), rule.getTrafficType(), true);
rule.getIcmpCode(), rule.getIcmpType(), rule.getRelated(), FirewallRuleType.System, rule.getNetworkId(), rule.getVpcId(), rule.getTrafficType(), true);
} catch (Exception e) {
logger.debug("Failed to add system wide firewall rule, due to:" + e.toString());
}

View File

@ -463,7 +463,8 @@ public class CommandSetupHelper {
cmds.addCommand(cmd);
}
public void createApplyFirewallRulesCommands(final List<? extends FirewallRule> rules, final VirtualRouter router, final Commands cmds, final long guestNetworkId) {
public void createApplyFirewallRulesCommands(final List<? extends FirewallRule> rules, final VirtualRouter router, final Commands cmds,
final Long guestNetworkId, final Long vpcId) {
final List<FirewallRuleTO> rulesTO = new ArrayList<>();
String systemRule = null;
Boolean defaultEgressPolicy = false;
@ -485,6 +486,10 @@ public class CommandSetupHelper {
final FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, srcIp, Purpose.Firewall, traffictype);
rulesTO.add(ruleTO);
} else if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) {
if (guestNetworkId == null) {
logger.warn("Skipping egress firewall rule {} as guestNetworkId is null", rule.getUuid());
continue;
}
final NetworkVO network = _networkDao.findById(guestNetworkId);
final NetworkOfferingVO offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
defaultEgressPolicy = offering.isEgressDefaultPolicy();
@ -495,9 +500,14 @@ public class CommandSetupHelper {
}
}
final SetFirewallRulesCommand cmd = new SetFirewallRulesCommand(rulesTO);
final SetFirewallRulesCommand cmd = new SetFirewallRulesCommand(rulesTO, vpcId);
cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(guestNetworkId, router.getId()));
if (guestNetworkId != null) {
cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(guestNetworkId, router.getId()));
}
if (vpcId != null) {
cmd.setAccessDetail(NetworkElementCommand.VPC_ID, String.valueOf(vpcId));
}
cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
@ -510,6 +520,10 @@ public class CommandSetupHelper {
cmds.addCommand(cmd);
}
public void createApplyFirewallRulesCommands(final List<? extends FirewallRule> rules, final VirtualRouter router, final Commands cmds, final long guestNetworkId) {
createApplyFirewallRulesCommands(rules, router, cmds, guestNetworkId, null);
}
public void createApplyIpv6FirewallRulesCommands(final List<? extends FirewallRule> rules, final VirtualRouter router, final Commands cmds, final long guestNetworkId) {
final List<FirewallRuleTO> rulesTO = new ArrayList<>();
String systemRule = null;
@ -551,7 +565,8 @@ public class CommandSetupHelper {
cmds.addCommand(cmd);
}
public void createFirewallRulesCommands(final List<? extends FirewallRule> rules, final VirtualRouter router, final Commands cmds, final long guestNetworkId) {
public void createFirewallRulesCommands(final List<? extends FirewallRule> rules, final VirtualRouter router, final Commands cmds, final Long guestNetworkId,
final Long vpcId) {
final List<FirewallRuleTO> rulesTO = new ArrayList<>();
String systemRule = null;
Boolean defaultEgressPolicy = false;
@ -573,6 +588,10 @@ public class CommandSetupHelper {
final FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, srcIp, Purpose.Firewall, traffictype);
rulesTO.add(ruleTO);
} else if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) {
if (guestNetworkId == null) {
logger.warn("Skipping egress firewall rule {} as guestNetworkId is null", rule.getUuid());
continue;
}
final NetworkVO network = _networkDao.findById(guestNetworkId);
final NetworkOfferingVO offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
defaultEgressPolicy = offering.isEgressDefaultPolicy();
@ -583,9 +602,14 @@ public class CommandSetupHelper {
}
}
final SetFirewallRulesCommand cmd = new SetFirewallRulesCommand(rulesTO);
final SetFirewallRulesCommand cmd = new SetFirewallRulesCommand(rulesTO, vpcId);
cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(guestNetworkId, router.getId()));
if (guestNetworkId != null) {
cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(guestNetworkId, router.getId()));
}
if (vpcId != null) {
cmd.setAccessDetail(NetworkElementCommand.VPC_ID, String.valueOf(vpcId));
}
cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
@ -598,6 +622,10 @@ public class CommandSetupHelper {
cmds.addCommand(cmd);
}
public void createFirewallRulesCommands(final List<? extends FirewallRule> rules, final VirtualRouter router, final Commands cmds, final Long guestNetworkId) {
createFirewallRulesCommands(rules, router, cmds, guestNetworkId, router.getVpcId());
}
public void createIpv6FirewallRulesCommands(final List<? extends FirewallRule> rules, final VirtualRouter router, final Commands cmds, final long guestNetworkId) {
final List<FirewallRuleTO> rulesTO = new ArrayList<>();
String systemRule = null;

View File

@ -2018,6 +2018,8 @@ Configurable, StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualM
} else {
buf.append(" has_public_network=false");
}
boolean isVpcFirewallEnabled = vpcManager.isProviderSupportServiceInVpc(vpc.getId(), Service.Firewall, Provider.VPCVirtualRouter);
buf.append(" vpc_firewall_enabled=").append(isVpcFirewallEnabled);
} else if (!publicNetwork) {
type = "dhcpsrvr";
} else {
@ -2497,7 +2499,7 @@ Configurable, StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualM
// Re-apply firewall Egress rules
logger.debug("Found " + firewallRulesEgress.size() + " firewall Egress rule(s) to apply as a part of domR " + router + " start.");
if (!firewallRulesEgress.isEmpty()) {
_commandSetupHelper.createFirewallRulesCommands(firewallRulesEgress, router, cmds, guestNetworkId);
_commandSetupHelper.createFirewallRulesCommands(firewallRulesEgress, router, cmds, guestNetworkId, router.getVpcId());
}
logger.debug(String.format("Found %d Ipv6 firewall rule(s) to apply as a part of domR %s start.", ipv6firewallRules.size(), router));
@ -2572,7 +2574,7 @@ Configurable, StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualM
// Re-apply firewall Ingress rules
logger.debug("Found " + firewallRulesIngress.size() + " firewall Ingress rule(s) to apply as a part of domR " + router + " start.");
if (!firewallRulesIngress.isEmpty()) {
_commandSetupHelper.createFirewallRulesCommands(firewallRulesIngress, router, cmds, guestNetworkId);
_commandSetupHelper.createFirewallRulesCommands(firewallRulesIngress, router, cmds, guestNetworkId, router.getVpcId());
}
// Re-apply port forwarding rules

View File

@ -72,6 +72,7 @@ import com.cloud.network.dao.LoadBalancerDao;
import com.cloud.network.dao.LoadBalancerVO;
import com.cloud.network.dao.MonitoringServiceVO;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.rules.FirewallRule;
import com.cloud.network.dao.RemoteAccessVpnVO;
import com.cloud.network.dao.Site2SiteVpnConnectionVO;
import com.cloud.network.lb.LoadBalancingRule;
@ -567,6 +568,10 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian
finalizeMonitorService(cmds, profile, domainRouterVO, provider, publicNics.get(0).second().getId(), true, routerHealthCheckConfig);
}
if (reprogramGuestNtwks) {
reapplyVpcFirewallIngressRules(cmds, domainRouterVO, provider);
}
for (final Pair<Nic, Network> nicNtwk : guestNics) {
final Nic guestNic = nicNtwk.first();
final long guestNetworkId = guestNic.getNetworkId();
@ -638,6 +643,26 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian
}
}
private void reapplyVpcFirewallIngressRules(final Commands cmds, final DomainRouterVO domainRouterVO, final Provider provider) {
final Long vpcId = domainRouterVO.getVpcId();
if (vpcId == null) {
return;
}
if (!_vpcMgr.isProviderSupportServiceInVpc(vpcId, Service.Firewall, provider)) {
return;
}
final List<FirewallRule> firewallRulesIngress = new ArrayList<>(
_rulesDao.listByVpcPurposeTrafficType(vpcId, FirewallRule.Purpose.Firewall, FirewallRule.TrafficType.Ingress));
if (firewallRulesIngress.isEmpty()) {
return;
}
logger.debug("Found {} VPC firewall ingress rule(s) to apply as a part of domR {} start for VPC {}", firewallRulesIngress.size(), domainRouterVO, vpcId);
_commandSetupHelper.createFirewallRulesCommands(firewallRulesIngress, domainRouterVO, cmds, null, vpcId);
}
protected boolean sendNetworkRulesToRouter(final long routerId, final long networkId, final boolean reprogramNetwork) throws ResourceUnavailableException {
final DomainRouterVO router = _routerDao.findById(routerId);
final Commands cmds = new Commands(OnError.Continue);

View File

@ -36,6 +36,7 @@ import com.cloud.network.lb.LoadBalancingRulesManager;
import com.cloud.network.router.VirtualRouter;
import com.cloud.network.rules.FirewallRule.Purpose;
import com.cloud.network.rules.LoadBalancerContainer.Scheme;
import com.cloud.network.vpc.Vpc;
import com.cloud.utils.net.Ip;
public class FirewallRules extends RuleApplier {
@ -50,6 +51,16 @@ public class FirewallRules extends RuleApplier {
_rules = rules;
}
public FirewallRules(final Network network, final Vpc vpc, final List<? extends FirewallRule> rules) {
super(network, vpc);
_rules = rules;
}
public FirewallRules(final Vpc vpc, final List<? extends FirewallRule> rules) {
super(null, vpc);
_rules = rules;
}
@Override
public boolean accept(final NetworkTopologyVisitor visitor, final VirtualRouter router) throws ResourceUnavailableException {
_router = router;

View File

@ -22,6 +22,7 @@ import org.apache.cloudstack.network.topology.NetworkTopologyVisitor;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.Network;
import com.cloud.network.router.VirtualRouter;
import com.cloud.network.vpc.Vpc;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
@ -30,16 +31,27 @@ public abstract class RuleApplier {
protected Logger logger = LogManager.getLogger(getClass());
protected Network _network;
protected Vpc _vpc;
protected VirtualRouter _router;
public RuleApplier(final Network network) {
_network = network;
_vpc = null;
}
public RuleApplier(final Network network, final Vpc vpc) {
_network = network;
_vpc = vpc;
}
public Network getNetwork() {
return _network;
}
public Vpc getVpc() {
return _vpc;
}
public VirtualRouter getRouter() {
return _router;
}

View File

@ -336,7 +336,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("VpcChecker"));
private List<VpcProvider> vpcElements = null;
private final List<Service> nonSupportedServices = Arrays.asList(Service.SecurityGroup, Service.Firewall);
private final List<Service> nonSupportedServices = Arrays.asList(Service.SecurityGroup);
private final List<Provider> supportedProviders = Arrays.asList(Provider.VPCVirtualRouter, Provider.NiciraNvp, Provider.InternalLbVm, Provider.Netscaler,
Provider.JuniperContrailVpcRouter, Provider.Ovs, Provider.BigSwitchBcf, Provider.ConfigDrive, Provider.Nsx, Provider.Netris);

View File

@ -989,15 +989,15 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
@Override
public boolean isVirtualRouterGateway(Network network) {
return isRoutedNetwork(network)
&& (networkServiceMapDao.canProviderSupportServiceInNetwork(network.getId(), Service.Gateway, Provider.VirtualRouter))
|| networkServiceMapDao.canProviderSupportServiceInNetwork(network.getId(), Service.Gateway, Provider.VPCVirtualRouter);
&& (networkServiceMapDao.canProviderSupportServiceInNetwork(network.getId(), Service.Gateway, Provider.VirtualRouter)
|| networkServiceMapDao.canProviderSupportServiceInNetwork(network.getId(), Service.Gateway, Provider.VPCVirtualRouter));
}
@Override
public boolean isVirtualRouterGateway(NetworkOffering networkOffering) {
return NetworkOffering.NetworkMode.ROUTED.equals(networkOffering.getNetworkMode())
&& networkOfferingServiceMapDao.canProviderSupportServiceInNetworkOffering(networkOffering.getId(), Service.Gateway, Provider.VirtualRouter)
|| networkOfferingServiceMapDao.canProviderSupportServiceInNetworkOffering(networkOffering.getId(), Service.Gateway, Provider.VPCVirtualRouter);
&& (networkOfferingServiceMapDao.canProviderSupportServiceInNetworkOffering(networkOffering.getId(), Service.Gateway, Provider.VirtualRouter)
|| networkOfferingServiceMapDao.canProviderSupportServiceInNetworkOffering(networkOffering.getId(), Service.Gateway, Provider.VPCVirtualRouter));
}
@Override

View File

@ -65,6 +65,7 @@ import com.cloud.network.rules.UserdataToRouterRules;
import com.cloud.network.vpc.NetworkACLItem;
import com.cloud.network.vpc.PrivateGateway;
import com.cloud.network.vpc.StaticRouteProfile;
import com.cloud.network.vpc.Vpc;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.DomainRouterVO;
import com.cloud.vm.NicProfile;
@ -228,6 +229,26 @@ public class BasicNetworkTopology implements NetworkTopology {
return applyRules(network, router, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper<RuleApplier>(firewallRules));
}
@Override
public boolean applyFirewallRulesInVPC(final Vpc vpc, final List<? extends FirewallRule> rules, final VirtualRouter router)
throws ResourceUnavailableException {
if (rules == null || rules.isEmpty()) {
logger.debug("No firewall rules to be applied for vpc {}", vpc);
return true;
}
logger.debug("APPLYING FIREWALL RULES");
final String typeString = "firewall rules";
final boolean isPodLevelException = false;
final boolean failWhenDisconnect = false;
final Long podId = null;
final FirewallRules firewallRules = new FirewallRules(vpc, rules);
return applyRulesInVPC(vpc, router, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper<RuleApplier>(firewallRules));
}
@Override
public boolean applyStaticNats(final Network network, final List<? extends StaticNat> rules, final VirtualRouter router) throws ResourceUnavailableException {
if (rules == null || rules.isEmpty()) {
@ -444,6 +465,102 @@ public class BasicNetworkTopology implements NetworkTopology {
return result;
}
@Override
public boolean applyRulesInVPC(final Vpc vpc, final VirtualRouter router, final String typeString, final boolean isPodLevelException, final Long podId,
final boolean failWhenDisconnect, final RuleApplierWrapper<RuleApplier> ruleApplierWrapper) throws ResourceUnavailableException {
if (vpc == null) {
throw new CloudRuntimeException("Unable to apply " + typeString + " because VPC is null");
}
if (router == null) {
logger.warn("Unable to apply {}, virtual router doesn't exist in vpc {}", typeString, vpc);
final Long dcId = vpc.getZoneId();
throw new ResourceUnavailableException("Unable to apply " + typeString, DataCenter.class, dcId);
}
final RuleApplier ruleApplier = ruleApplierWrapper.getRuleType();
final Long dcId = vpc.getZoneId();
final DataCenter dc = _dcDao.findById(dcId);
final boolean isZoneBasic = dc.getNetworkType() == NetworkType.Basic;
// isPodLevelException and podId is only used for basic zone
assert !(!isZoneBasic && isPodLevelException || isZoneBasic && isPodLevelException && podId == null);
final List<VirtualRouter> connectedRouters = new ArrayList<VirtualRouter>();
final List<VirtualRouter> disconnectedRouters = new ArrayList<VirtualRouter>();
boolean result = true;
final String msg = "Unable to apply " + typeString + " on disconnected router ";
if (router.getState() == State.Running) {
logger.debug("Applying {} in vpc {}", typeString, vpc);
if (router.isStopPending()) {
if (_hostDao.findById(router.getHostId()).getState() == Status.Up) {
throw new ResourceUnavailableException("Unable to process due to the stop pending router " + router.getInstanceName()
+ " haven't been stopped after it's host coming back!", DataCenter.class, router.getDataCenterId());
}
logger.debug("Router {} is stop pending, so not sending apply {} commands to the backend", router, typeString);
return false;
}
try {
result = ruleApplier.accept(getVisitor(), router);
connectedRouters.add(router);
} catch (final AgentUnavailableException e) {
logger.warn("{}{}", msg, router, e);
disconnectedRouters.add(router);
}
// If rules fail to apply on one domR and not due to
// disconnection, no need to proceed with the rest
if (!result) {
if (isZoneBasic && isPodLevelException) {
throw new ResourceUnavailableException("Unable to apply " + typeString + " on router ", Pod.class, podId);
}
throw new ResourceUnavailableException("Unable to apply " + typeString + " on router ", DataCenter.class, router.getDataCenterId());
}
} else if (router.getState() == State.Stopped || router.getState() == State.Stopping) {
logger.debug("Router {} is in {}, so not sending apply {} commands to the backend", router, router.getState(), typeString);
} else {
logger.warn("Unable to apply " + typeString + ", virtual router is not in the right state " + router.getState());
if (isZoneBasic && isPodLevelException) {
throw new ResourceUnavailableException("Unable to apply " + typeString + ", virtual router is not in the right state", Pod.class, podId);
}
throw new ResourceUnavailableException("Unable to apply " + typeString + ", virtual router is not in the right state", DataCenter.class, router.getDataCenterId());
}
if (!connectedRouters.isEmpty()) {
// Shouldn't we include this check inside the method?
if (!isZoneBasic && !disconnectedRouters.isEmpty()) {
// These disconnected redundant virtual routers are out of sync
// now, stop them for synchronization
for (final VirtualRouter virtualRouter : disconnectedRouters) {
// If we have at least 1 disconnected redundant router, callhandleSingleWorkingRedundantRouter().
if (virtualRouter.getIsRedundantRouter()) {
_networkHelper.handleSingleWorkingRedundantRouter(connectedRouters, disconnectedRouters, msg);
break;
}
}
}
} else if (!disconnectedRouters.isEmpty()) {
if (logger.isDebugEnabled()) {
logger.debug("{}{}", msg, router);
}
if (isZoneBasic && isPodLevelException) {
throw new ResourceUnavailableException(msg, Pod.class, podId);
}
throw new ResourceUnavailableException(msg, DataCenter.class, disconnectedRouters.get(0).getDataCenterId());
}
result = true;
if (failWhenDisconnect) {
result = !connectedRouters.isEmpty();
}
return result;
}
@Override
public boolean removeDhcpEntry(Network network, NicProfile nic, VirtualMachineProfile profile, VirtualRouter virtualRouter) throws ResourceUnavailableException {
logger.debug("REMOVING DHCP ENTRY RULE");

View File

@ -145,7 +145,9 @@ public class BasicNetworkVisitor extends NetworkTopologyVisitor {
} else if (purpose == Purpose.Firewall) {
_commandSetupHelper.createApplyFirewallRulesCommands(rules, router, cmds, network.getId());
final Long guestNetworkId = network != null ? network.getId() : null;
final Long vpcId = network != null ? network.getVpcId() : router.getVpcId();
_commandSetupHelper.createApplyFirewallRulesCommands(rules, router, cmds, guestNetworkId, vpcId);
return _networkGeneralHelper.sendCommandsToRouter(router, cmds);

View File

@ -35,6 +35,7 @@ import com.cloud.network.rules.StaticNat;
import com.cloud.network.vpc.NetworkACLItem;
import com.cloud.network.vpc.PrivateGateway;
import com.cloud.network.vpc.StaticRouteProfile;
import com.cloud.network.vpc.Vpc;
import com.cloud.vm.DomainRouterVO;
import com.cloud.vm.NicProfile;
import com.cloud.vm.VirtualMachineProfile;
@ -72,6 +73,8 @@ public interface NetworkTopology {
boolean applyFirewallRules(final Network network, final List<? extends FirewallRule> rules, final VirtualRouter router) throws ResourceUnavailableException;
boolean applyFirewallRulesInVPC(final Vpc vpc, final List<? extends FirewallRule> rules, final VirtualRouter router) throws ResourceUnavailableException;
boolean applyStaticNats(final Network network, final List<? extends StaticNat> rules, final VirtualRouter router) throws ResourceUnavailableException;
boolean associatePublicIP(final Network network, final List<? extends PublicIpAddress> ipAddress, final VirtualRouter router) throws ResourceUnavailableException;
@ -89,6 +92,9 @@ public interface NetworkTopology {
boolean applyRules(final Network network, final VirtualRouter router, final String typeString, final boolean isPodLevelException, final Long podId,
final boolean failWhenDisconnect, RuleApplierWrapper<RuleApplier> ruleApplier) throws ResourceUnavailableException;
boolean applyRulesInVPC(final Vpc vpc, final VirtualRouter router, final String typeString, final boolean isPodLevelException, final Long podId,
final boolean failWhenDisconnect, RuleApplierWrapper<RuleApplier> ruleApplier) throws ResourceUnavailableException;
boolean removeDhcpEntry(final Network network, final NicProfile nic, final VirtualMachineProfile profile, final VirtualRouter virtualRouter) throws ResourceUnavailableException;
boolean applyBgpPeers(final Network network, final List<? extends BgpPeer> bpgPeers, final VirtualRouter virtualRouter) throws ResourceUnavailableException;

View File

@ -19,10 +19,13 @@ package com.cloud.network;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ -54,8 +57,13 @@ import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.rules.FirewallRule;
import com.cloud.network.rules.StaticNat;
import com.cloud.network.rules.StaticNatImpl;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.network.vpc.VpcVO;
import com.cloud.network.vpc.VpcManager;
import com.cloud.dc.dao.VlanDao;
import com.cloud.offerings.NetworkOfferingVO;
import com.cloud.offerings.dao.NetworkOfferingDao;
import com.cloud.user.AccountVO;
@ -105,6 +113,15 @@ public class IpAddressManagerTest {
@Mock
AccountManager accountManagerMock;
@Mock
VpcManager vpcMgr;
@Mock
VpcDao vpcDao;
@Mock
VlanDao vlanDao;
final long dummyID = 1L;
final String UUID = "uuid";
@ -492,4 +509,135 @@ public class IpAddressManagerTest {
Assert.assertTrue(result);
}
private FirewallRule makeRule(Long networkId, Long vpcId, FirewallRule.Purpose purpose,
FirewallRule.TrafficType trafficType) {
FirewallRule rule = mock(FirewallRule.class);
lenient().when(rule.getNetworkId()).thenReturn(networkId);
lenient().when(rule.getVpcId()).thenReturn(vpcId);
lenient().when(rule.getPurpose()).thenReturn(purpose);
lenient().when(rule.getTrafficType()).thenReturn(trafficType);
return rule;
}
/** Stub the two IP-association helper methods so they are no-ops. */
private void stubIpAssocHelpers() throws ResourceUnavailableException {
doReturn(false).when(ipAddressManager).checkIfIpAssocRequired(any(Network.class), anyBoolean(), any());
}
/**
* Test: Non-VPC rules still resolve via networkId (backward compatibility).
*/
@Test
public void applyRulesNonVpcRuleStillWorksViaNetworkId() throws ResourceUnavailableException {
long networkId = 10L;
NetworkVO network = mock(NetworkVO.class);
when(network.getId()).thenReturn(networkId);
when(networkDao.findById(networkId)).thenReturn(network);
FirewallRule rule = makeRule(networkId, null, FirewallRule.Purpose.Firewall, FirewallRule.TrafficType.Ingress);
NetworkRuleApplier applier = mock(NetworkRuleApplier.class);
when(ipAddressDao.listByAssociatedNetwork(networkId, null)).thenReturn(new ArrayList<>());
stubIpAssocHelpers();
boolean result = ipAddressManager.applyRules(
Collections.singletonList(rule), FirewallRule.Purpose.Firewall, applier, false);
assertTrue(result);
verify(networkDao).findById(networkId);
verify(applier).applyRules(network, null, FirewallRule.Purpose.Firewall, Collections.singletonList(rule));
}
/**
* Test: VPC rule resolves network via VpcManager.getVpcNetworks()
* when networkId is null but vpcId is set.
*/
@Test
public void applyRulesVpcRuleResolvesNetworkViaVpcManager() throws ResourceUnavailableException {
long vpcId = 20L;
VpcVO vpc = mock(VpcVO.class);
when(vpcDao.findById(vpcId)).thenReturn(vpc);
FirewallRule rule = makeRule(null, vpcId, FirewallRule.Purpose.Firewall, FirewallRule.TrafficType.Ingress);
NetworkRuleApplier applier = mock(NetworkRuleApplier.class);
stubIpAssocHelpers();
boolean result = ipAddressManager.applyRules(
Collections.singletonList(rule), FirewallRule.Purpose.Firewall, applier, false);
assertTrue(result);
verify(vpcDao).findById(vpcId);
verify(applier).applyRules(null, vpc, FirewallRule.Purpose.Firewall, Collections.singletonList(rule));
}
/**
* Test: For VPC egress firewall rules, IP collection should be skipped.
*/
@Test
public void applyRulesVpcEgressFirewallRuleSkipsIpCollection() throws ResourceUnavailableException {
long vpcId = 20L;
VpcVO vpc = mock(VpcVO.class);
when(vpcDao.findById(vpcId)).thenReturn(vpc);
FirewallRule rule = makeRule(null, vpcId, FirewallRule.Purpose.Firewall, FirewallRule.TrafficType.Egress);
NetworkRuleApplier applier = mock(NetworkRuleApplier.class);
stubIpAssocHelpers();
boolean result = ipAddressManager.applyRules(
Collections.singletonList(rule), FirewallRule.Purpose.Firewall, applier, false);
assertTrue(result);
verify(ipAddressDao, never()).listByAssociatedVpc(anyLong(), any());
verify(applier).applyRules(null, vpc, FirewallRule.Purpose.Firewall, Collections.singletonList(rule));
}
/**
* Test: VPC ingress firewall rules collect public IPs from VPC (listByAssociatedVpc),
* NOT from network (listByAssociatedNetwork).
*/
@Test
public void applyRulesVpcIngressRuleCollectsIpsFromVpcNotNetwork() throws ResourceUnavailableException {
long vpcId = 20L;
VpcVO vpc = mock(VpcVO.class);
when(vpcDao.findById(vpcId)).thenReturn(vpc);
stubIpAssocHelpers();
NetworkRuleApplier applier = mock(NetworkRuleApplier.class);
FirewallRule rule = makeRule(null, vpcId, FirewallRule.Purpose.Firewall, FirewallRule.TrafficType.Ingress);
ipAddressManager.applyRules(Collections.singletonList(rule), FirewallRule.Purpose.Firewall, applier, false);
verify(applier).applyRules(null, vpc, FirewallRule.Purpose.Firewall, Collections.singletonList(rule));
verify(ipAddressDao, never()).listByAssociatedVpc(vpcId, null);
verify(ipAddressDao, never()).listByAssociatedNetwork(anyLong(), any());
}
/**
* Test: Error handling respects continueOnError flag.
* When continueOnError=true, exceptions are caught and false is returned.
*/
@Test
public void applyRulesVpcRuleErrorHandlingWithContinueOnErrorTrue() throws ResourceUnavailableException {
long vpcId = 20L;
VpcVO vpc = mock(VpcVO.class);
when(vpcDao.findById(vpcId)).thenReturn(vpc);
stubIpAssocHelpers();
NetworkRuleApplier applier = mock(NetworkRuleApplier.class);
when(applier.applyRules(any(), any(), any(), any())).thenThrow(new ResourceUnavailableException("test", Network.class, 0L));
FirewallRule rule = makeRule(null, vpcId, FirewallRule.Purpose.Firewall, FirewallRule.TrafficType.Ingress);
boolean result = ipAddressManager.applyRules(
Collections.singletonList(rule), FirewallRule.Purpose.Firewall, applier, true);
assertFalse(result);
}
}

View File

@ -19,10 +19,13 @@ package com.cloud.network.element;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.Network;
import com.cloud.network.NetworkModel;
import com.cloud.network.RemoteAccessVpn;
import com.cloud.network.VpnUser;
import com.cloud.network.router.VpcVirtualNetworkApplianceManagerImpl;
import com.cloud.network.vpc.Vpc;
import com.cloud.network.vpc.VpcManager;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.utils.db.EntityManager;
import com.cloud.vm.DomainRouterVO;
@ -43,7 +46,9 @@ import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ -60,6 +65,12 @@ public class VpcVirtualRouterElementTest {
@Mock
EntityManager _entityMgr;
@Mock
NetworkModel _networkMdl;
@Mock
VpcManager _vpcMgr;
@Mock
NetworkTopologyContext networkTopologyContext;
@ -188,4 +199,19 @@ public class VpcVirtualRouterElementTest {
verify(remoteAccessVpn, times(1)).getVpcId();
}
@Test
public void testCanHandleFirewallUsesVpcCapability() {
final Network network = Mockito.mock(Network.class);
when(_networkMdl.getPhysicalNetworkId(network)).thenReturn(1L);
when(network.getId()).thenReturn(200L);
when(network.getVpcId()).thenReturn(100L);
when(_networkMdl.isProviderEnabledInPhysicalNetwork(1L, Network.Provider.VPCVirtualRouter.getName())).thenReturn(true);
when(_networkMdl.isProviderSupportServiceInNetwork(200L, Network.Service.Firewall, Network.Provider.VPCVirtualRouter)).thenReturn(true);
assertTrue(vpcVirtualRouterElement.canHandle(network, Network.Service.Firewall));
verify(_networkMdl).isProviderSupportServiceInNetwork(200L, Network.Service.Firewall, Network.Provider.VPCVirtualRouter);
verify(_vpcMgr, never()).isProviderSupportServiceInVpc(100L, Network.Service.Firewall, Network.Provider.VPCVirtualRouter);
}
}

View File

@ -17,27 +17,36 @@
package com.cloud.network.firewall;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.NetworkRuleConflictException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.IpAddressManager;
import com.cloud.network.Network;
import com.cloud.network.Network.Capability;
import com.cloud.network.Network.Service;
import com.cloud.network.NetworkModel;
import com.cloud.network.NetworkRuleApplier;
import com.cloud.network.dao.FirewallRulesDao;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.element.FirewallServiceProvider;
import com.cloud.network.element.VirtualRouterElement;
import com.cloud.network.element.VpcVirtualRouterElement;
import com.cloud.network.rules.FirewallRule;
import com.cloud.network.rules.FirewallRule.FirewallRuleType;
import com.cloud.network.rules.FirewallRule.Purpose;
import com.cloud.network.rules.FirewallRuleVO;
import com.cloud.network.vpc.Vpc;
import com.cloud.network.vpc.VpcManager;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.DomainManager;
import com.cloud.utils.component.ComponentContext;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.network.RoutedIpv4Manager;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@ -53,12 +62,18 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ -79,9 +94,13 @@ public class FirewallManagerTest {
@Mock
IpAddressManager _ipAddrMgr;
@Mock
RoutedIpv4Manager routedIpv4Manager;
@Mock
FirewallRulesDao _firewallDao;
@Mock
NetworkDao _networkDao;
@Mock
IPAddressDao _ipAddressDao;
@Spy
@InjectMocks
@ -115,7 +134,7 @@ public class FirewallManagerTest {
}
private FirewallRule createFirewallRule(int startPort, int endPort, Purpose purpose) {
return new FirewallRuleVO("xid", 1L, startPort, endPort, "TCP", 2, 3, 4, purpose, new ArrayList<>(),
return new FirewallRuleVO("xid", 1L, startPort, endPort, "TCP", 2L, 3, 4, purpose, new ArrayList<>(),
new ArrayList<>(), 5, 6, null, FirewallRule.TrafficType.Ingress);
}
@ -332,4 +351,376 @@ public class FirewallManagerTest {
Assert.assertFalse(result);
}
@Test
public void testValidateFirewallRuleVpcWithoutAssociatedNetworkUsesVpcCapabilities() {
final Account caller = Mockito.mock(Account.class);
final IPAddressVO ipAddress = Mockito.mock(IPAddressVO.class);
final NetworkVO network = Mockito.mock(NetworkVO.class);
final FirewallServiceProvider firewallServiceProvider = Mockito.mock(FirewallServiceProvider.class);
final Map<Capability, String> firewallCaps = new HashMap<>();
final Map<Service, Map<Capability, String>> capabilities = new HashMap<>();
firewallCaps.put(Capability.SupportedTrafficDirection, "ingress, egress");
firewallCaps.put(Capability.SupportedProtocols, "tcp,udp,icmp");
firewallCaps.put(Capability.SupportedEgressProtocols, "tcp,udp,icmp");
capabilities.put(Service.Firewall, firewallCaps);
when(ipAddress.getVpcId()).thenReturn(10L);
when(_networkModel.getNetwork(2L)).thenReturn(network);
when(network.getVpcId()).thenReturn(10L);
when(routedIpv4Manager.isVirtualRouterGateway(network)).thenReturn(false);
when(firewallServiceProvider.getProvider()).thenReturn(Network.Provider.VPCVirtualRouter);
when(firewallServiceProvider.getCapabilities()).thenReturn(capabilities);
when(_vpcMgr.isProviderSupportServiceInVpc(10L, Service.Firewall, Network.Provider.VPCVirtualRouter)).thenReturn(true);
_firewallMgr._firewallElements = List.of(firewallServiceProvider);
_firewallMgr.validateFirewallRule(caller, ipAddress, 80, 80, "tcp", Purpose.Firewall, FirewallRuleType.User, 2L, FirewallRule.TrafficType.Ingress);
verify(_networkModel, Mockito.never()).getNetworkServiceCapabilities(Mockito.anyLong(), Mockito.eq(Service.Firewall));
}
@Test
public void testIsVpcIpAddressReturnsTrueWhenVpcIdPresent() {
IPAddressVO ipAddress = Mockito.mock(IPAddressVO.class);
when(ipAddress.getVpcId()).thenReturn(5L);
Assert.assertTrue(_firewallMgr.isVpcIpAddress(ipAddress));
}
@Test
public void testIsVpcIpAddressReturnsFalseWhenVpcIdNull() {
IPAddressVO ipAddress = Mockito.mock(IPAddressVO.class);
when(ipAddress.getVpcId()).thenReturn(null);
Assert.assertFalse(_firewallMgr.isVpcIpAddress(ipAddress));
}
@Test
public void testValidateFirewallRuleForIsolatedIpReturnsNetworkId() {
IPAddressVO ipAddress = Mockito.mock(IPAddressVO.class);
when(ipAddress.getAssociatedWithNetworkId()).thenReturn(42L);
Long result = _firewallMgr.validateFirewallRuleForIsolatedIp(ipAddress);
Assert.assertEquals(Long.valueOf(42L), result);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateFirewallRuleForIsolatedIpThrowsWhenNotAssociated() {
IPAddressVO ipAddress = Mockito.mock(IPAddressVO.class);
when(ipAddress.getAssociatedWithNetworkId()).thenReturn(null);
_firewallMgr.validateFirewallRuleForIsolatedIp(ipAddress);
}
@Test
public void testValidateFirewallRuleForVpcIpReturnsNetworkId() {
IPAddressVO ipAddress = Mockito.mock(IPAddressVO.class);
Long result = _firewallMgr.validateFirewallRuleForVpcIp(ipAddress, 99L);
Assert.assertEquals(Long.valueOf(99L), result);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateFirewallRuleForVpcIpThrowsWhenNetworkIdNull() {
IPAddressVO ipAddress = Mockito.mock(IPAddressVO.class);
_firewallMgr.validateFirewallRuleForVpcIp(ipAddress, null);
}
@Test
public void testGetFirewallServiceCapabilitiesForNonVpcNetworkUsesNetworkModel() {
NetworkVO network = Mockito.mock(NetworkVO.class);
when(network.getId()).thenReturn(1L);
when(network.getVpcId()).thenReturn(null);
Map<Capability, String> caps = new HashMap<>();
caps.put(Capability.SupportedProtocols, "tcp,udp");
when(_networkModel.getNetworkServiceCapabilities(1L, Service.Firewall)).thenReturn(caps);
Map<Network.Capability, String> result = _firewallMgr.getFirewallServiceCapabilities(network);
Assert.assertEquals(caps, result);
verify(_networkModel, times(1)).getNetworkServiceCapabilities(1L, Service.Firewall);
}
@Test
public void testGetFirewallServiceCapabilitiesForVpcNetworkUsesVpcProvider() {
NetworkVO network = Mockito.mock(NetworkVO.class);
FirewallServiceProvider fwProvider = Mockito.mock(FirewallServiceProvider.class);
Map<Capability, String> firewallCaps = new HashMap<>();
firewallCaps.put(Capability.SupportedProtocols, "tcp,udp,icmp");
Map<Service, Map<Capability, String>> providerCapabilities = new HashMap<>();
providerCapabilities.put(Service.Firewall, firewallCaps);
when(network.getVpcId()).thenReturn(10L);
when(fwProvider.getProvider()).thenReturn(Network.Provider.VPCVirtualRouter);
when(fwProvider.getCapabilities()).thenReturn(providerCapabilities);
when(_vpcMgr.isProviderSupportServiceInVpc(10L, Service.Firewall, Network.Provider.VPCVirtualRouter)).thenReturn(true);
_firewallMgr._firewallElements = List.of(fwProvider);
Map<Network.Capability, String> result = _firewallMgr.getFirewallServiceCapabilities(network);
Assert.assertEquals(firewallCaps, result);
verify(_networkModel, never()).getNetworkServiceCapabilities(Mockito.anyLong(), Mockito.eq(Service.Firewall));
}
@Test
public void testGetFirewallServiceCapabilitiesForVpcNetworkFallsBackToNetworkModelWhenNoProvider() {
NetworkVO network = Mockito.mock(NetworkVO.class);
FirewallServiceProvider fwProvider = Mockito.mock(FirewallServiceProvider.class);
Map<Capability, String> fallbackCaps = new HashMap<>();
when(network.getId()).thenReturn(1L);
when(network.getVpcId()).thenReturn(10L);
when(fwProvider.getProvider()).thenReturn(Network.Provider.VPCVirtualRouter);
when(_vpcMgr.isProviderSupportServiceInVpc(10L, Service.Firewall, Network.Provider.VPCVirtualRouter)).thenReturn(false);
when(_networkModel.getNetworkServiceCapabilities(1L, Service.Firewall)).thenReturn(fallbackCaps);
_firewallMgr._firewallElements = List.of(fwProvider);
Map<Network.Capability, String> result = _firewallMgr.getFirewallServiceCapabilities(network);
Assert.assertEquals(fallbackCaps, result);
verify(_networkModel, times(1)).getNetworkServiceCapabilities(1L, Service.Firewall);
}
@Test
public void testGetFirewallServiceCapabilitiesForVpcReturnsCapabilitiesWhenProviderSupports() {
FirewallServiceProvider fwProvider = Mockito.mock(FirewallServiceProvider.class);
Map<Capability, String> firewallCaps = new HashMap<>();
firewallCaps.put(Capability.SupportedProtocols, "tcp,udp");
Map<Service, Map<Capability, String>> caps = new HashMap<>();
caps.put(Service.Firewall, firewallCaps);
when(fwProvider.getProvider()).thenReturn(Network.Provider.VPCVirtualRouter);
when(fwProvider.getCapabilities()).thenReturn(caps);
when(_vpcMgr.isProviderSupportServiceInVpc(10L, Service.Firewall, Network.Provider.VPCVirtualRouter)).thenReturn(true);
_firewallMgr._firewallElements = List.of(fwProvider);
Map<Network.Capability, String> result = _firewallMgr.getFirewallServiceCapabilitiesForVpc(10L);
Assert.assertNotNull(result);
Assert.assertEquals("tcp,udp", result.get(Capability.SupportedProtocols));
}
@Test
public void testGetFirewallServiceCapabilitiesForVpcReturnsNullWhenNoProviderSupports() {
FirewallServiceProvider fwProvider = Mockito.mock(FirewallServiceProvider.class);
when(fwProvider.getProvider()).thenReturn(Network.Provider.VPCVirtualRouter);
when(_vpcMgr.isProviderSupportServiceInVpc(10L, Service.Firewall, Network.Provider.VPCVirtualRouter)).thenReturn(false);
_firewallMgr._firewallElements = List.of(fwProvider);
Map<Network.Capability, String> result = _firewallMgr.getFirewallServiceCapabilitiesForVpc(10L);
Assert.assertNull(result);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateFirewallRuleForVpcThrowsOnInvalidStartPort() {
Account caller = Mockito.mock(Account.class);
_firewallMgr.validateFirewallRuleForVpc(caller, null, -1, 80, "tcp", Purpose.Firewall, FirewallRuleType.User, 10L, FirewallRule.TrafficType.Ingress);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateFirewallRuleForVpcThrowsOnInvalidEndPort() {
Account caller = Mockito.mock(Account.class);
_firewallMgr.validateFirewallRuleForVpc(caller, null, 80, 70000, "tcp", Purpose.Firewall, FirewallRuleType.User, 10L, FirewallRule.TrafficType.Ingress);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateFirewallRuleForVpcThrowsWhenStartPortGreaterThanEndPort() {
Account caller = Mockito.mock(Account.class);
_firewallMgr.validateFirewallRuleForVpc(caller, null, 200, 100, "tcp", Purpose.Firewall, FirewallRuleType.User, 10L, FirewallRule.TrafficType.Ingress);
}
@Test
public void testValidateFirewallRuleForVpcSystemTypeWithNullIpReturnsEarly() {
// System rule type + null IP should return without further validation
Account caller = Mockito.mock(Account.class);
// Should not throw even though vpcId checks come after this
_firewallMgr.validateFirewallRuleForVpc(caller, null, 80, 80, "tcp", Purpose.Firewall, FirewallRuleType.System, 10L, FirewallRule.TrafficType.Ingress);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateFirewallRuleForVpcThrowsWhenVpcIdNullAndNotSystemRule() {
Account caller = Mockito.mock(Account.class);
IPAddressVO ipAddress = Mockito.mock(IPAddressVO.class);
_firewallMgr.validateFirewallRuleForVpc(caller, ipAddress, 80, 80, "tcp", Purpose.Firewall, FirewallRuleType.User, null, FirewallRule.TrafficType.Ingress);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateFirewallRuleForVpcThrowsWhenActiveVpcNotFound() {
Account caller = Mockito.mock(Account.class);
IPAddressVO ipAddress = Mockito.mock(IPAddressVO.class);
when(_vpcMgr.getActiveVpc(10L)).thenReturn(null);
_firewallMgr.validateFirewallRuleForVpc(caller, ipAddress, 80, 80, "tcp", Purpose.Firewall, FirewallRuleType.User, 10L, FirewallRule.TrafficType.Ingress);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateFirewallRuleForVpcThrowsWhenFirewallServiceNotSupported() {
Account caller = Mockito.mock(Account.class);
IPAddressVO ipAddress = Mockito.mock(IPAddressVO.class);
Vpc vpc = Mockito.mock(Vpc.class);
when(_vpcMgr.getActiveVpc(10L)).thenReturn(vpc);
_firewallMgr._firewallElements = Collections.emptyList();
_firewallMgr.validateFirewallRuleForVpc(caller, ipAddress, 80, 80, "tcp", Purpose.Firewall, FirewallRuleType.User, 10L, FirewallRule.TrafficType.Ingress);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateFirewallRuleForVpcThrowsOnUnsupportedProtocol() {
Account caller = Mockito.mock(Account.class);
IPAddressVO ipAddress = Mockito.mock(IPAddressVO.class);
Vpc vpc = Mockito.mock(Vpc.class);
FirewallServiceProvider fwProvider = Mockito.mock(FirewallServiceProvider.class);
Map<Capability, String> firewallCaps = new HashMap<>();
firewallCaps.put(Capability.SupportedProtocols, "tcp,udp");
firewallCaps.put(Capability.SupportedTrafficDirection, "ingress,egress");
Map<Service, Map<Capability, String>> caps = new HashMap<>();
caps.put(Service.Firewall, firewallCaps);
when(_vpcMgr.getActiveVpc(10L)).thenReturn(vpc);
when(fwProvider.getProvider()).thenReturn(Network.Provider.VPCVirtualRouter);
when(fwProvider.getCapabilities()).thenReturn(caps);
when(_vpcMgr.isProviderSupportServiceInVpc(10L, Service.Firewall, Network.Provider.VPCVirtualRouter)).thenReturn(true);
_firewallMgr._firewallElements = List.of(fwProvider);
_firewallMgr.validateFirewallRuleForVpc(caller, ipAddress, 80, 80, "gre", Purpose.Firewall, FirewallRuleType.User, 10L, FirewallRule.TrafficType.Ingress);
}
@Test
public void testValidateFirewallRuleForVpcSucceedsWithSupportedProtocolAndTrafficType() {
Account caller = Mockito.mock(Account.class);
IPAddressVO ipAddress = Mockito.mock(IPAddressVO.class);
Vpc vpc = Mockito.mock(Vpc.class);
FirewallServiceProvider fwProvider = Mockito.mock(FirewallServiceProvider.class);
Map<Capability, String> firewallCaps = new HashMap<>();
firewallCaps.put(Capability.SupportedProtocols, "tcp,udp,icmp");
firewallCaps.put(Capability.SupportedTrafficDirection, "ingress,egress");
Map<Service, Map<Capability, String>> caps = new HashMap<>();
caps.put(Service.Firewall, firewallCaps);
when(_vpcMgr.getActiveVpc(10L)).thenReturn(vpc);
when(fwProvider.getProvider()).thenReturn(Network.Provider.VPCVirtualRouter);
when(fwProvider.getCapabilities()).thenReturn(caps);
when(_vpcMgr.isProviderSupportServiceInVpc(10L, Service.Firewall, Network.Provider.VPCVirtualRouter)).thenReturn(true);
_firewallMgr._firewallElements = List.of(fwProvider);
// Should not throw
_firewallMgr.validateFirewallRuleForVpc(caller, ipAddress, 80, 80, "tcp", Purpose.Firewall, FirewallRuleType.User, 10L, FirewallRule.TrafficType.Ingress);
verify(_accountMgr, times(1)).checkAccess(caller, null, true, ipAddress);
}
@Test
public void testCreateFirewallRuleRoutesToVpcWhenVpcIdProvided() throws NetworkRuleConflictException {
Account caller = Mockito.mock(Account.class);
FirewallRule vpcRule = Mockito.mock(FirewallRule.class);
doReturn(vpcRule).when(_firewallMgr).createFirewallRuleForVpc(
Mockito.anyLong(), Mockito.eq(caller), Mockito.any(), Mockito.anyInt(), Mockito.anyInt(),
Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.any(), Mockito.any(FirewallRuleType.class), Mockito.anyLong(),
Mockito.any(FirewallRule.TrafficType.class), Mockito.anyBoolean());
_firewallMgr.createFirewallRule(1L, caller, "xid", 80, 80, "tcp",
Collections.singletonList("0.0.0.0/0"), null, null, null, null,
FirewallRuleType.User, null, 10L, FirewallRule.TrafficType.Ingress, true);
verify(_firewallMgr, times(1)).createFirewallRuleForVpc(
Mockito.anyLong(), Mockito.eq(caller), Mockito.any(), Mockito.anyInt(), Mockito.anyInt(),
Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.any(), Mockito.any(FirewallRuleType.class), Mockito.anyLong(),
Mockito.any(FirewallRule.TrafficType.class), Mockito.anyBoolean());
verify(_firewallMgr, never()).createFirewallRuleForNonVPC(
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
}
@Test
public void testCreateFirewallRuleRoutesToNonVpcWhenVpcIdNull() throws NetworkRuleConflictException {
Account caller = Mockito.mock(Account.class);
FirewallRule nonVpcRule = Mockito.mock(FirewallRule.class);
doReturn(nonVpcRule).when(_firewallMgr).createFirewallRuleForNonVPC(
Mockito.any(), Mockito.eq(caller), Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.any(), Mockito.any(FirewallRuleType.class), Mockito.anyLong(),
Mockito.any(FirewallRule.TrafficType.class), Mockito.anyBoolean());
_firewallMgr.createFirewallRule(null, caller, "xid", 80, 80, "tcp",
Collections.singletonList("0.0.0.0/0"), null, null, null, null,
FirewallRuleType.User, 2L, null, FirewallRule.TrafficType.Ingress, true);
verify(_firewallMgr, times(1)).createFirewallRuleForNonVPC(
Mockito.any(), Mockito.eq(caller), Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.any(), Mockito.any(FirewallRuleType.class), Mockito.anyLong(),
Mockito.any(FirewallRule.TrafficType.class), Mockito.anyBoolean());
verify(_firewallMgr, never()).createFirewallRuleForVpc(
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
}
@Test
public void testApplyRulesForVpcNetworkUsesVpcProviderCheck() throws ResourceUnavailableException {
FirewallManagerImpl firewallMgr = new FirewallManagerImpl();
firewallMgr._networkModel = _networkModel;
firewallMgr._vpcMgr = _vpcMgr;
Network network = Mockito.mock(Network.class);
FirewallServiceProvider fwProvider = Mockito.mock(FirewallServiceProvider.class);
List<FirewallRule> rules = new ArrayList<>();
FirewallRuleVO rule = new FirewallRuleVO("rule1", 1L, 80, 80, "tcp", 1L, 2, 3, Purpose.Firewall,
Collections.emptyList(), Collections.emptyList(), null, null, null, FirewallRule.TrafficType.Ingress);
rules.add(rule);
when(network.getVpcId()).thenReturn(10L);
when(fwProvider.getProvider()).thenReturn(Network.Provider.VPCVirtualRouter);
when(_vpcMgr.isProviderSupportServiceInVpc(10L, Service.Firewall, Network.Provider.VPCVirtualRouter)).thenReturn(true);
when(fwProvider.applyFWRules(Mockito.eq(network), Mockito.anyList())).thenReturn(true);
firewallMgr._firewallElements = List.of(fwProvider);
boolean result = firewallMgr.applyRules(network, Purpose.Firewall, rules);
Assert.assertTrue(result);
verify(_vpcMgr, times(1)).isProviderSupportServiceInVpc(10L, Service.Firewall, Network.Provider.VPCVirtualRouter);
verify(_networkModel, never()).isProviderSupportServiceInNetwork(Mockito.anyLong(), Mockito.eq(Service.Firewall), Mockito.any());
}
@Test
public void testApplyRulesForNonVpcNetworkUsesNetworkModelProviderCheck() throws ResourceUnavailableException {
FirewallManagerImpl firewallMgr = new FirewallManagerImpl();
firewallMgr._networkModel = _networkModel;
firewallMgr._vpcMgr = _vpcMgr;
Network network = Mockito.mock(Network.class);
FirewallServiceProvider fwProvider = Mockito.mock(FirewallServiceProvider.class);
List<FirewallRule> rules = new ArrayList<>();
FirewallRuleVO rule = new FirewallRuleVO("rule1", 1L, 80, 80, "tcp", 1L, 2, 3, Purpose.Firewall,
Collections.emptyList(), Collections.emptyList(), null, null, null, FirewallRule.TrafficType.Ingress);
rules.add(rule);
when(network.getId()).thenReturn(1L);
when(network.getVpcId()).thenReturn(null);
when(fwProvider.getProvider()).thenReturn(Network.Provider.VirtualRouter);
when(_networkModel.isProviderSupportServiceInNetwork(1L, Service.Firewall, Network.Provider.VirtualRouter)).thenReturn(true);
when(fwProvider.applyFWRules(Mockito.eq(network), Mockito.anyList())).thenReturn(true);
firewallMgr._firewallElements = List.of(fwProvider);
boolean result = firewallMgr.applyRules(network, Purpose.Firewall, rules);
Assert.assertTrue(result);
verify(_networkModel, times(1)).isProviderSupportServiceInNetwork(1L, Service.Firewall, Network.Provider.VirtualRouter);
verify(_vpcMgr, never()).isProviderSupportServiceInVpc(Mockito.anyLong(), Mockito.eq(Service.Firewall), Mockito.any());
}
@Test
public void testGetSourceIpForIngressRuleReturnsNullWhenIdIsNull() {
IPAddressVO result = _firewallMgr.getSourceIpForIngressRule(null);
Assert.assertNull(result);
}
@Test(expected = CloudRuntimeException.class)
public void testGetSourceIpForIngressRuleReturnsNullWhenIpIsnotPresent() {
when(_ipAddressDao.findById(1L)).thenReturn(null);
_firewallMgr.getSourceIpForIngressRule(1L);
}
}

View File

@ -703,14 +703,139 @@ class CsAcl(CsDataBag):
self.add_routing_rules()
return
desired_firewall_ips = set()
fw_chains_created = set()
if self.config.is_vpc() and self.config.is_vpc_firewall_enabled():
desired_firewall_ips = self._get_desired_vpc_firewall_ips()
# Pre-create FIREWALL chains for ALL public IPs that have any active rule
# (static NAT, port forwarding, LB, or explicit firewall rule) so that the
# default DROP is always in place even before any explicit firewall rule exists.
self._ensure_vpc_firewall_chains(desired_firewall_ips, fw_chains_created)
for item in self.dbag:
if item == "id":
continue
if self.config.is_vpc():
if self.config.is_vpc() and not ("purpose" in self.dbag[item] and self.dbag[item]["purpose"] == "Firewall"):
self.AclDevice(self.dbag[item], self.config).create()
else:
if self.config.is_vpc() and self.dbag[item].get("purpose") == "Firewall" and not self.config.is_vpc_firewall_enabled():
continue
# Chain skeleton is already ensured by the pre-creation pass above;
# _ensure_vpc_firewall_chains is idempotent (skips IPs in fw_chains_created).
if self.config.is_vpc() and self.config.is_vpc_firewall_enabled() and self.dbag[item].get("purpose") == "Firewall":
src_ip = self.dbag[item].get("src_ip")
self._ensure_vpc_firewall_chains([src_ip], fw_chains_created)
self.AclIP(self.dbag[item], self.config).create()
if self.config.is_vpc() and self.config.is_vpc_firewall_enabled():
self._cleanup_removed_vpc_firewall_chains(desired_firewall_ips)
def _get_desired_vpc_firewall_ips(self):
"""
Collect the full set of public IPs that should have a FIREWALL mangle chain
in a VPC with firewall capability. This includes IPs from explicit firewall
rules, forwarding/static-NAT rules, and load-balancer rules.
"""
if not self.config.is_vpc():
return set()
ips = set()
ips.update(self._get_firewall_rule_ips())
ips.update(self._get_forwarding_rule_ips())
ips.update(self._get_loadbalancer_ips())
return ips
def _get_firewall_rule_ips(self):
"""Return public IPs that have explicit firewall rules in this data bag."""
ips = set()
for item in self.dbag:
if item == "id":
continue
rule = self.dbag[item]
if rule.get("purpose") == "Firewall":
src_ip = rule.get("src_ip")
if src_ip:
ips.add(src_ip)
return ips
def _get_forwarding_rule_ips(self):
"""
Return public IPs from the forwardingrules bag (static NAT and port forwarding).
That bag is keyed by public IP, so each key (other than 'id') is a public IP.
"""
ips = set()
try:
fwd_bag = CsDataBag("forwardingrules", self.config)
for public_ip in fwd_bag.get_bag():
if public_ip == "id":
continue
ips.add(public_ip)
except Exception as e:
logging.debug("Could not load forwardingrules for VPC firewall chain collection: %s", e)
return ips
def _get_loadbalancer_ips(self):
"""
Return public IPs from the loadbalancer bag.
add_rules entries are formatted as 'ip:port', so the IP is the first segment.
"""
ips = set()
try:
lb_bag = CsDataBag("loadbalancer", self.config)
lb_data = lb_bag.get_bag()
if "config" in lb_data and lb_data["config"]:
for rule_str in lb_data["config"][0].get("add_rules", []):
ip = rule_str.split(":")[0]
if ip:
ips.add(ip)
except Exception as e:
logging.debug("Could not load loadbalancer for VPC firewall chain collection: %s", e)
return ips
def _ensure_vpc_firewall_chains(self, source_ips, fw_chains_created):
fw = self.config.get_fw()
for src_ip in source_ips:
if not src_ip or src_ip in fw_chains_created:
continue
fw.append(["mangle", "front",
"-A PREROUTING -d %s/32 -j FIREWALL_%s" % (src_ip, src_ip)])
fw.append(["mangle", "front",
"-A FIREWALL_%s -m state --state RELATED,ESTABLISHED -j RETURN" % src_ip])
fw.append(["mangle", "",
"-A FIREWALL_%s -j DROP" % src_ip])
fw_chains_created.add(src_ip)
def _cleanup_removed_vpc_firewall_chains(self, desired_firewall_ips):
try:
mangle_save = CsHelper.execute("iptables-save -t mangle")
existing_firewall_ips = []
for line in mangle_save:
if line.startswith(":FIREWALL_"):
chain = line.split(" ")[0][1:]
existing_firewall_ips.append(chain.replace("FIREWALL_", "", 1))
for src_ip in existing_firewall_ips:
if src_ip in desired_firewall_ips:
continue
self._delete_vpc_firewall_chain(src_ip)
except Exception as e:
logging.debug("Failed VPC firewall chain cleanup: %s", e)
def _delete_vpc_firewall_chain(self, src_ip):
chain = "FIREWALL_%s" % src_ip
try:
prerouting_rules = CsHelper.execute("iptables -t mangle -S PREROUTING")
for rule in prerouting_rules:
if ("-d %s/32" % src_ip) in rule and ("-j %s" % chain) in rule:
delete_rule = rule.replace("-A PREROUTING", "-D PREROUTING", 1)
CsHelper.execute2("iptables -t mangle %s" % delete_rule, False)
CsHelper.execute2("iptables -t mangle -F %s" % chain, False)
CsHelper.execute2("iptables -t mangle -X %s" % chain, False)
logging.info("Deleted VPC firewall chain %s as last firewall rule was removed", chain)
except Exception as e:
logging.debug("Failed deleting VPC firewall chain %s: %s", chain, e)
class CsIpv6Firewall(CsDataBag):
"""
Deal with IPv6 Firewall

View File

@ -680,6 +680,7 @@ class CsIP:
self.fw.append(["filter", "", "-P INPUT DROP"])
self.fw.append(["filter", "", "-P FORWARD DROP"])
def fw_router_routing(self):
if self.config.is_vpc() or not self.config.is_routed():
return

View File

@ -155,3 +155,6 @@ class CsConfig(object):
def has_public_network(self):
return self.cmdline().idata().get('has_public_network', 'true') == 'true'
def is_vpc_firewall_enabled(self):
return self.cmdline().idata().get('vpc_firewall_enabled', 'false') == 'true'

View File

@ -0,0 +1,187 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""Smoke tests for firewall rules on VPC public IPs."""
from nose.plugins.attrib import attr
from marvin.cloudstackTestCase import cloudstackTestCase
from marvin.lib.base import Account, FireWallRule, Network, NetworkOffering, PublicIPAddress, VPC, VpcOffering
from marvin.lib.common import get_domain, get_zone, list_publicIP
from marvin.lib.utils import cleanup_resources, wait_until
class TestVpcFirewallRules(cloudstackTestCase):
@classmethod
def setUpClass(cls):
cls.testClient = super(TestVpcFirewallRules, cls).getClsTestClient()
cls.apiclient = cls.testClient.getApiClient()
cls.services = cls.testClient.getParsedTestDataConfig()
cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
cls.domain = get_domain(cls.apiclient)
cls._cleanup = []
cls.account = Account.create(
cls.apiclient,
cls.services["account"],
domainid=cls.domain.id
)
cls._cleanup.append(cls.account)
cls.services["vpc_offering"]["supportedservices"] = (
"Vpn,Dhcp,Dns,SourceNat,Lb,UserData,StaticNat,"
"NetworkACL,PortForwarding,Firewall"
)
cls.services["vpc_offering"]["serviceProviderList"] = {
"Vpn": "VpcVirtualRouter",
"Dhcp": "VpcVirtualRouter",
"Dns": "VpcVirtualRouter",
"SourceNat": "VpcVirtualRouter",
"Lb": "VpcVirtualRouter",
"UserData": "VpcVirtualRouter",
"StaticNat": "VpcVirtualRouter",
"NetworkACL": "VpcVirtualRouter",
"PortForwarding": "VpcVirtualRouter",
"Firewall": "VpcVirtualRouter"
}
cls.vpc_offering = VpcOffering.create(
cls.apiclient,
cls.services["vpc_offering"]
)
cls.vpc_offering.update(cls.apiclient, state="Enabled")
cls._cleanup.append(cls.vpc_offering)
network_offering = NetworkOffering.list(
cls.apiclient,
name="DefaultIsolatedNetworkOfferingForVpcNetworks"
)
cls.assertTrue(
network_offering is not None and len(network_offering) > 0,
"No VPC tier network offering found"
)
cls.network_offering = network_offering[0]
cls.services["vpc"]["cidr"] = "10.20.30.0/24"
cls.vpc = VPC.create(
cls.apiclient,
cls.services["vpc"],
vpcofferingid=cls.vpc_offering.id,
zoneid=cls.zone.id,
account=cls.account.name,
domainid=cls.account.domainid
)
cls.tier = Network.create(
cls.apiclient,
services={
"name": "vpc-fw-tier",
"displaytext": "vpc-fw-tier"
},
accountid=cls.account.name,
domainid=cls.account.domainid,
networkofferingid=cls.network_offering.id,
zoneid=cls.zone.id,
vpcid=cls.vpc.id,
gateway="10.20.30.1",
netmask="255.255.255.0"
)
@classmethod
def tearDownClass(cls):
try:
cleanup_resources(cls.apiclient, cls._cleanup)
except Exception as e:
raise Exception("Warning: Exception during cleanup: %s" % e)
def setUp(self):
self.apiclient = self.testClient.getApiClient()
self.cleanup = []
def tearDown(self):
cleanup_resources(self.apiclient, self.cleanup)
def _wait_for_firewall_rule(self, rule_id):
rules = FireWallRule.list(self.apiclient, id=rule_id, listall=True)
if rules and len(rules) == 1:
return True, rules[0]
return False, None
@attr(tags=["advanced", "advancedns", "smoke"], required_hardware="false")
def test_01_create_firewall_rule_on_vpc_public_ip(self):
"""Verify firewall rule can be created and listed on a dedicated VPC public IP."""
public_ip = PublicIPAddress.create(
self.apiclient,
zoneid=self.zone.id,
accountid=self.account.name,
domainid=self.account.domainid,
vpcid=self.vpc.id
)
self.cleanup.append(public_ip)
firewall_rule = FireWallRule.create(
self.apiclient,
ipaddressid=public_ip.ipaddress.id,
protocol="tcp",
cidrlist=["0.0.0.0/0"],
startport=19090,
endport=19090,
vpcid=self.vpc.id
)
self.cleanup.insert(0, firewall_rule)
result, listed_rule = wait_until(2, 10, self._wait_for_firewall_rule, firewall_rule.id)
self.assertTrue(result, "Firewall rule was not listed for the VPC public IP")
self.assertEqual(listed_rule.id, firewall_rule.id)
self.assertEqual(listed_rule.ipaddressid, public_ip.ipaddress.id)
self.assertEqual(listed_rule.vpcid, self.vpc.id)
self.assertEqual(listed_rule.protocol.lower(), "tcp")
self.assertEqual(int(listed_rule.startport), 19090)
self.assertEqual(int(listed_rule.endport), 19090)
@attr(tags=["advanced", "advancedns", "smoke"], required_hardware="false")
def test_02_create_firewall_rule_on_vpc_source_nat_ip(self):
"""Verify firewall rule can be created and listed on the VPC source NAT IP."""
source_nat_ips = list_publicIP(
self.apiclient,
vpcid=self.vpc.id,
listall=True,
issourcenat=True
)
self.assertTrue(source_nat_ips is not None and len(source_nat_ips) > 0,
"No source NAT IP found for the VPC")
source_nat_ip = source_nat_ips[0]
firewall_rule = FireWallRule.create(
self.apiclient,
ipaddressid=source_nat_ip.id,
protocol="tcp",
cidrlist=["0.0.0.0/0"],
startport=19443,
endport=19443,
vpcid=self.vpc.id
)
self.cleanup.append(firewall_rule)
result, listed_rule = wait_until(2, 10, self._wait_for_firewall_rule, firewall_rule.id)
self.assertTrue(result, "Firewall rule was not listed for the VPC source NAT IP")
self.assertEqual(listed_rule.id, firewall_rule.id)
self.assertEqual(listed_rule.ipaddressid, source_nat_ip.id)
self.assertEqual(listed_rule.vpcid, self.vpc.id)
self.assertEqual(listed_rule.protocol.lower(), "tcp")
self.assertEqual(int(listed_rule.startport), 19443)
self.assertEqual(int(listed_rule.endport), 19443)

View File

@ -136,23 +136,39 @@ export default {
}
if (this.resource && this.resource.vpcid) {
const vpc = await this.fetchVpc()
const hasFirewallCapability = this.hasVpcFirewallCapability(vpc)
// VPC IPs with source nat have only VPN when VPC offering conserve mode = false
if (this.resource.issourcenat && vpc?.vpcofferingconservemode === false) {
this.tabs = this.defaultTabs.concat(this.$route.meta.tabs.filter(tab => tab.name === 'vpn'))
const tabs = this.defaultTabs.concat(this.$route.meta.tabs.filter(tab => tab.name === 'vpn'))
this.tabs = hasFirewallCapability ? this.addFirewallTab(tabs) : tabs
return
}
// VPC IPs with static nat have nothing
// VPC IPs with static nat keep existing VPN behavior; show firewall only when capability exists
if (this.resource.isstaticnat) {
if (this.resource.virtualmachinetype === 'DomainRouter') {
this.tabs = this.defaultTabs.concat(this.$route.meta.tabs.filter(tab => tab.name === 'vpn'))
let tabs = this.$route.meta.tabs
if (hasFirewallCapability) {
tabs = this.addFirewallTab(tabs).map(tab => {
if (tab.name !== 'firewall') {
return tab
}
const staticNatFirewallTab = { ...tab }
delete staticNatFirewallTab.networkServiceFilter
return staticNatFirewallTab
})
} else {
tabs = tabs.filter(tab => tab.name !== 'firewall')
}
this.tabs = tabs
return
}
// VPC IPs don't have firewall
let tabs = this.$route.meta.tabs.filter(tab => tab.name !== 'firewall')
// VPC IPs have all tabs; firewall is shown only if VPC has firewall capability
let tabs = this.$route.meta.tabs
if (!hasFirewallCapability) {
tabs = tabs.filter(tab => tab.name !== 'firewall')
}
const network = await this.fetchNetwork()
if (network && network.networkofferingconservemode) {
@ -168,12 +184,12 @@ export default {
this.portFWRuleCount = await this.fetchPortFWRule()
this.loadBalancerRuleCount = await this.fetchLoadBalancerRule()
// VPC IPs with PF only have PF
// VPC IPs with PF only have PF (and firewall)
if (this.portFWRuleCount > 0) {
tabs = tabs.filter(tab => tab.name !== 'loadbalancing')
}
// VPC IPs with LB rules only have LB
// VPC IPs with LB rules only have LB (and firewall)
if (this.loadBalancerRuleCount > 0) {
tabs = tabs.filter(tab => tab.name !== 'portforwarding')
}
@ -200,6 +216,17 @@ export default {
fetchAction () {
this.actions = this.$route.meta.actions || []
},
addFirewallTab (tabs) {
const firewallTab = this.$route.meta.tabs.find(tab => tab.name === 'firewall')
if (!firewallTab || tabs.some(tab => tab.name === 'firewall')) {
return tabs
}
return tabs.concat(firewallTab)
},
hasVpcFirewallCapability (vpc) {
const services = vpc?.service || []
return Array.isArray(services) && services.some(service => (service?.name || '').toLowerCase() === 'firewall')
},
fetchVpc () {
if (!this.resource.vpcid) {
return null

View File

@ -946,6 +946,9 @@ export default {
provider.enabled = self.isVpcCoreProvider(provider.name, svc.name) ||
!self.isBuiltInNetworkProvider(provider.name)
}
if (svc.name === 'Firewall' && provider.name === 'VpcVirtualRouter') {
provider.enabled = false
}
} else { // *** non-vpc ***
provider.enabled = !['InternalLbVm', 'VpcVirtualRouter', 'Nsx', 'Netris'].includes(provider.name)
}

View File

@ -432,6 +432,9 @@ export default {
})
},
isVpcCoreProvider (providerName, serviceName) {
if (serviceName === 'Firewall') {
return ['VpcVirtualRouter'].includes(providerName)
}
if (['VpcVirtualRouter', 'Netscaler', 'BigSwitchBcf', 'ConfigDrive'].includes(providerName)) {
return true
}
@ -540,7 +543,7 @@ export default {
this.supportedServices = []
this.supportedServiceLoading = true
getAPI('listSupportedNetworkServices').then(json => {
const vpcServices = ['Dhcp', 'Dns', 'Lb', 'Gateway', 'StaticNat', 'SourceNat', 'NetworkACL', 'PortForwarding', 'UserData', 'Vpn', 'Connectivity', 'CustomAction']
const vpcServices = ['Dhcp', 'Dns', 'Lb', 'Gateway', 'StaticNat', 'SourceNat', 'NetworkACL', 'PortForwarding', 'UserData', 'Vpn', 'Connectivity', 'CustomAction', 'Firewall']
services = (json?.listsupportednetworkservicesresponse?.networkservice || [])
.filter(service => vpcServices.includes(service.name))
.map(service => {
@ -575,7 +578,7 @@ export default {
this.supportedServices = []
if (this.networkmode === 'ROUTED') {
services = services.filter(service => !['SourceNat', 'StaticNat', 'Lb', 'PortForwarding', 'Vpn'].includes(service.name))
services = services.filter(service => !['SourceNat', 'StaticNat', 'Lb', 'PortForwarding', 'Vpn', 'Firewall'].includes(service.name))
}
this.supportedServices = services
}).catch(error => {

View File

@ -523,6 +523,7 @@ export default {
return [
{ name: 'Dhcp', provider: [{ name: 'VpcVirtualRouter' }, { name: 'ConfigDrive' }] },
{ name: 'Dns', provider: [{ name: 'VpcVirtualRouter' }, { name: 'ConfigDrive' }] },
{ name: 'Firewall', provider: [{ name: 'VpcVirtualRouter' }] },
{ name: 'Lb', provider: [{ name: 'VpcVirtualRouter' }, { name: 'InternalLbVm' }] },
{ name: 'Gateway', provider: [{ name: 'VpcVirtualRouter' }, { name: 'BigSwitchBcf' }] },
{ name: 'StaticNat', provider: [{ name: 'VpcVirtualRouter' }, { name: 'BigSwitchBcf' }] },