mirror of
https://github.com/apache/cloudstack
synced 2026-08-01 21:25:23 +00:00
Support Firewall for public IPs in VPC (#12706)
This commit is contained in:
parent
f2a1f839a7
commit
13822b1ac1
@ -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;
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,7 +69,9 @@ public interface FirewallRule extends ControlledEntity, Identity, InternalIdenti
|
||||
|
||||
State getState();
|
||||
|
||||
long getNetworkId();
|
||||
Long getNetworkId();
|
||||
|
||||
Long getVpcId();
|
||||
|
||||
Long getSourceIpAddressId();
|
||||
|
||||
|
||||
@ -212,7 +212,7 @@ public class CreateEgressFirewallRuleCmd extends BaseAsyncCreateCmd implements F
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNetworkId() {
|
||||
public Long getNetworkId() {
|
||||
return networkId;
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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>();
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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>();
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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'
|
||||
|
||||
187
test/integration/smoke/test_vpc_firewall_rules.py
Normal file
187
test/integration/smoke/test_vpc_firewall_rules.py
Normal 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)
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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 => {
|
||||
|
||||
@ -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' }] },
|
||||
|
||||
Loading…
Reference in New Issue
Block a user