bug 1067:

- covered basic impelementation for xen, need to test corner cases.
      - Not implemneted: kvm, vmware , listing of egress rules.
This commit is contained in:
Naredula Janardhana Reddy 2011-08-19 11:05:46 +05:30
parent 2c1df02ba1
commit 4369b0ba96
20 changed files with 1593 additions and 12 deletions

View File

@ -0,0 +1,47 @@
/**
* Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
*
* This software is licensed under the GNU General Public License v3 or later.
*
* It is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.cloud.agent.api;
public class SecurityEgressRuleAnswer extends Answer {
Long logSequenceNumber = null;
Long vmId = null;
protected SecurityEgressRuleAnswer() {
}
public SecurityEgressRuleAnswer(SecurityEgressRulesCmd cmd) {
super(cmd);
this.logSequenceNumber = cmd.getSeqNum();
this.vmId = cmd.getVmId();
}
public SecurityEgressRuleAnswer(SecurityEgressRulesCmd cmd, boolean result, String detail) {
super(cmd, result, detail);
this.logSequenceNumber = cmd.getSeqNum();
this.vmId = cmd.getVmId();
}
public Long getLogSequenceNumber() {
return logSequenceNumber;
}
public Long getVmId() {
return vmId;
}
}

View File

@ -0,0 +1,144 @@
/**
* Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
*
* This software is licensed under the GNU General Public License v3 or later.
*
* It is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.cloud.agent.api;
public class SecurityEgressRulesCmd extends Command {
public static class EgressIpPortAndProto {
String proto;
int startPort;
int endPort;
String [] allowedCidrs;
public EgressIpPortAndProto() { }
public EgressIpPortAndProto(String proto, int startPort, int endPort,
String[] allowedCidrs) {
super();
this.proto = proto;
this.startPort = startPort;
this.endPort = endPort;
this.allowedCidrs = allowedCidrs;
}
public String[] getAllowedCidrs() {
return allowedCidrs;
}
public void setAllowedCidrs(String[] allowedCidrs) {
this.allowedCidrs = allowedCidrs;
}
public String getProto() {
return proto;
}
public int getStartPort() {
return startPort;
}
public int getEndPort() {
return endPort;
}
}
String guestIp;
String vmName;
String guestMac;
String signature;
Long seqNum;
Long vmId;
EgressIpPortAndProto [] ruleSet;
public SecurityEgressRulesCmd() {
super();
}
public SecurityEgressRulesCmd(String guestIp, String guestMac, String vmName, Long vmId, String signature, Long seqNum, EgressIpPortAndProto[] ruleSet) {
super();
this.guestIp = guestIp;
this.vmName = vmName;
this.ruleSet = ruleSet;
this.guestMac = guestMac;
this.signature = signature;
this.seqNum = seqNum;
this.vmId = vmId;
}
@Override
public boolean executeInSequence() {
return true;
}
public EgressIpPortAndProto[] getRuleSet() {
return ruleSet;
}
public void setRuleSet(EgressIpPortAndProto[] ruleSet) {
this.ruleSet = ruleSet;
}
public String getGuestIp() {
return guestIp;
}
public String getVmName() {
return vmName;
}
public String stringifyRules() {
StringBuilder ruleBuilder = new StringBuilder();
for (SecurityEgressRulesCmd.EgressIpPortAndProto ipPandP: getRuleSet()) {
ruleBuilder.append(ipPandP.getProto()).append(":").append(ipPandP.getStartPort()).append(":").append(ipPandP.getEndPort()).append(":");
for (String cidr: ipPandP.getAllowedCidrs()) {
ruleBuilder.append(cidr).append(",");
}
ruleBuilder.append("NEXT");
ruleBuilder.append(" ");
}
return ruleBuilder.toString();
}
public String getSignature() {
return signature;
}
public String getGuestMac() {
return guestMac;
}
public Long getSeqNum() {
return seqNum;
}
public Long getVmId() {
return vmId;
}
}

View File

@ -82,6 +82,7 @@ import com.cloud.network.rules.LoadBalancer;
import com.cloud.network.rules.PortForwardingRule;
import com.cloud.network.rules.StaticNatRule;
import com.cloud.network.security.IngressRule;
import com.cloud.network.security.EgressRule;
import com.cloud.network.security.SecurityGroup;
import com.cloud.network.security.SecurityGroupRules;
import com.cloud.offering.DiskOffering;
@ -176,6 +177,8 @@ public interface ResponseGenerator {
SecurityGroupResponse createSecurityGroupResponseFromIngressRule(List<? extends IngressRule> ingressRules);
SecurityGroupResponse createSecurityGroupResponseFromEgressRule(List<? extends EgressRule> egressRules);
SecurityGroupResponse createSecurityGroupResponse(SecurityGroup group);
ExtractResponse createExtractResponse(Long uploadId, Long id, Long zoneId, Long accountId, String mode);

View File

@ -0,0 +1,233 @@
/**
* Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
*
* This software is licensed under the GNU General Public License v3 or later.
*
* It is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.cloud.api.commands;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import com.cloud.api.ApiConstants;
import com.cloud.api.BaseAsyncCmd;
import com.cloud.api.BaseCmd;
import com.cloud.api.Implementation;
import com.cloud.api.Parameter;
import com.cloud.api.ServerApiException;
import com.cloud.api.response.EgressRuleResponse;
import com.cloud.api.response.SecurityGroupResponse;
import com.cloud.async.AsyncJob;
import com.cloud.event.EventTypes;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.network.security.EgressRule;
import com.cloud.user.Account;
import com.cloud.user.UserContext;
import com.cloud.utils.StringUtils;
@Implementation(responseObject = EgressRuleResponse.class, description = "Authorizes a particular ingress rule for this security group")
@SuppressWarnings("rawtypes")
public class AuthorizeSecurityGroupEgressCmd extends BaseAsyncCmd {
public static final Logger s_logger = Logger.getLogger(AuthorizeSecurityGroupEgressCmd.class.getName());
private static final String s_name = "authorizesecuritygroupingress";
// ///////////////////////////////////////////////////
// ////////////// API parameters /////////////////////
// ///////////////////////////////////////////////////
@Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "TCP is default. UDP is the other supported protocol")
private String protocol;
@Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "start port for this ingress rule")
private Integer startPort;
@Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "end port for this ingress rule")
private Integer endPort;
@Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "type of the icmp message being sent")
private Integer icmpType;
@Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "error code for this icmp message")
private Integer icmpCode;
@Parameter(name=ApiConstants.CIDR_LIST, type=CommandType.LIST, collectionType=CommandType.STRING, description="the cidr list associated")
private List cidrList;
@Parameter(name = ApiConstants.USER_SECURITY_GROUP_LIST, type = CommandType.MAP, description = "user to security group mapping")
private Map userSecurityGroupList;
@Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="an optional domainId for the security group. If the account parameter is used, domainId must also be used.")
private Long domainId;
@Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="an optional account for the virtual machine. Must be used with domainId.")
private String accountName;
@Parameter(name=ApiConstants.SECURITY_GROUP_ID, type=CommandType.LONG, description="The ID of the security group. Mutually exclusive with securityGroupName parameter")
private Long securityGroupId;
@Parameter(name=ApiConstants.SECURITY_GROUP_NAME, type=CommandType.STRING, description="The name of the security group. Mutually exclusive with securityGroupName parameter")
private String securityGroupName;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public String getAccountName() {
return accountName;
}
public List getCidrList() {
return cidrList;
}
public Integer getEndPort() {
return endPort;
}
public Integer getIcmpCode() {
return icmpCode;
}
public Integer getIcmpType() {
return icmpType;
}
public Long getSecurityGroupId() {
if (securityGroupId != null && securityGroupName != null) {
throw new InvalidParameterValueException("securityGroupId and securityGroupName parameters are mutually exclusive");
}
if (securityGroupName != null) {
securityGroupId = _responseGenerator.getSecurityGroupId(securityGroupName, getEntityOwnerId());
if (securityGroupId == null) {
throw new InvalidParameterValueException("Unable to find security group " + securityGroupName + " for account id=" + getEntityOwnerId());
}
securityGroupName = null;
}
if (securityGroupId == null) {
throw new InvalidParameterValueException("Either securityGroupId or securityGroupName is required by authorizeSecurityGroupEgress command");
}
return securityGroupId;
}
public String getProtocol() {
if (protocol == null) {
return "all";
}
return protocol;
}
public Integer getStartPort() {
return startPort;
}
public Map getUserSecurityGroupList() {
return userSecurityGroupList;
}
// ///////////////////////////////////////////////////
// ///////////// API Implementation///////////////////
// ///////////////////////////////////////////////////
@Override
public String getCommandName() {
return s_name;
}
public static String getResultObjectName() {
return "securitygroup";
}
@Override
public long getEntityOwnerId() {
Account account = UserContext.current().getCaller();
if ((account == null) || isAdmin(account.getType())) {
if ((domainId != null) && (accountName != null)) {
Account userAccount = _responseGenerator.findAccountByNameDomain(accountName, domainId);
if (userAccount != null) {
return userAccount.getId();
} else {
throw new InvalidParameterValueException("Unable to find account by name " + accountName + " in domain " + domainId);
}
}
}
return account.getId();
}
@Override
public String getEventType() {
return EventTypes.EVENT_SECURITY_GROUP_AUTHORIZE_INGRESS;
}
@Override
public String getEventDescription() {
StringBuilder sb = new StringBuilder();
if (getUserSecurityGroupList() != null) {
sb.append("group list(group/account): ");
Collection userGroupCollection = getUserSecurityGroupList().values();
Iterator iter = userGroupCollection.iterator();
HashMap userGroup = (HashMap) iter.next();
String group = (String) userGroup.get("group");
String authorizedAccountName = (String) userGroup.get("account");
sb.append(group + "/" + authorizedAccountName);
while (iter.hasNext()) {
userGroup = (HashMap) iter.next();
group = (String) userGroup.get("group");
authorizedAccountName = (String) userGroup.get("account");
sb.append(", " + group + "/" + authorizedAccountName);
}
} else if (getCidrList() != null) {
sb.append("cidr list: ");
sb.append(StringUtils.join(getCidrList(), ", "));
} else {
sb.append("<error: no ingress parameters>");
}
return "authorizing ingress to group: " + getSecurityGroupId() + " to " + sb.toString();
}
@Override
public void execute() {
List<? extends EgressRule> egressRules = _securityGroupService.authorizeSecurityGroupEgress(this);
if (egressRules != null && !egressRules.isEmpty()) {
SecurityGroupResponse response = _responseGenerator.createSecurityGroupResponseFromEgressRule(egressRules);
this.setResponseObject(response);
} else {
throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to authorize security group ingress rule(s)");
}
}
@Override
public AsyncJob.Type getInstanceType() {
return AsyncJob.Type.SecurityGroup;
}
@Override
public Long getInstanceId() {
return getSecurityGroupId();
}
}

View File

@ -0,0 +1,109 @@
/**
* Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
*
* This software is licensed under the GNU General Public License v3 or later.
*
* It is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.cloud.api.commands;
import org.apache.log4j.Logger;
import com.cloud.api.ApiConstants;
import com.cloud.api.BaseAsyncCmd;
import com.cloud.api.BaseCmd;
import com.cloud.api.Implementation;
import com.cloud.api.Parameter;
import com.cloud.api.ServerApiException;
import com.cloud.api.response.SuccessResponse;
import com.cloud.async.AsyncJob;
import com.cloud.event.EventTypes;
import com.cloud.network.security.SecurityGroup;
import com.cloud.user.Account;
@Implementation(responseObject = SuccessResponse.class, description = "Deletes a particular ingress rule from this security group")
public class RevokeSecurityGroupEgressCmd extends BaseAsyncCmd {
public static final Logger s_logger = Logger.getLogger(RevokeSecurityGroupEgressCmd.class.getName());
private static final String s_name = "revokesecuritygroupingress";
// ///////////////////////////////////////////////////
// ////////////// API parameters /////////////////////
// ///////////////////////////////////////////////////
@Parameter(name = ApiConstants.ID, type = CommandType.LONG, required = true, description = "The ID of the ingress rule")
private Long id;
// ///////////////////////////////////////////////////
// ///////////////// Accessors ///////////////////////
// ///////////////////////////////////////////////////
public Long getId() {
return id;
}
// ///////////////////////////////////////////////////
// ///////////// API Implementation///////////////////
// ///////////////////////////////////////////////////
@Override
public String getCommandName() {
return s_name;
}
public static String getResultObjectName() {
return "revokesecuritygroupingress";
}
@Override
public long getEntityOwnerId() {
SecurityGroup group = _entityMgr.findById(SecurityGroup.class, getId());
if (group != null) {
return group.getAccountId();
}
return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked
}
@Override
public String getEventType() {
return EventTypes.EVENT_SECURITY_GROUP_REVOKE_INGRESS;
}
@Override
public String getEventDescription() {
return "revoking ingress rule id: " + getId();
}
@Override
public void execute() {
boolean result = _securityGroupService.revokeSecurityGroupEgress(this);
if (result) {
SuccessResponse response = new SuccessResponse(getCommandName());
this.setResponseObject(response);
} else {
throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to revoke security group ingress rule");
}
}
@Override
public AsyncJob.Type getInstanceType() {
return AsyncJob.Type.SecurityGroup;
}
@Override
public Long getInstanceId() {
return getId();
}
}

View File

@ -0,0 +1,123 @@
/**
* Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
*
* This software is licensed under the GNU General Public License v3 or later.
*
* It is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.cloud.api.response;
import com.cloud.api.ApiConstants;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
public class EgressRuleResponse extends BaseResponse {
@SerializedName("ruleid") @Param(description="the id of the ingress rule")
private Long ruleId;
@SerializedName("protocol") @Param(description="the protocol of the ingress rule")
private String protocol;
@SerializedName(ApiConstants.ICMP_TYPE) @Param(description="the type of the ICMP message response")
private Integer icmpType;
@SerializedName(ApiConstants.ICMP_CODE) @Param(description="the code for the ICMP message response")
private Integer icmpCode;
@SerializedName(ApiConstants.START_PORT) @Param(description="the starting IP of the ingress rule")
private Integer startPort;
@SerializedName(ApiConstants.END_PORT) @Param(description="the ending IP of the ingress rule ")
private Integer endPort;
@SerializedName(ApiConstants.SECURITY_GROUP_NAME) @Param(description="security group name")
private String securityGroupName;
@SerializedName(ApiConstants.ACCOUNT) @Param(description="account owning the ingress rule")
private String accountName;
@SerializedName(ApiConstants.CIDR) @Param(description="the CIDR notation for the base IP address of the ingress rule")
private String cidr;
public Long getRuleId() {
return ruleId;
}
public void setRuleId(Long ruleId) {
this.ruleId = ruleId;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public Integer getIcmpType() {
return icmpType;
}
public void setIcmpType(Integer icmpType) {
this.icmpType = icmpType;
}
public Integer getIcmpCode() {
return icmpCode;
}
public void setIcmpCode(Integer icmpCode) {
this.icmpCode = icmpCode;
}
public Integer getStartPort() {
return startPort;
}
public void setStartPort(Integer startPort) {
this.startPort = startPort;
}
public Integer getEndPort() {
return endPort;
}
public void setEndPort(Integer endPort) {
this.endPort = endPort;
}
public String getSecurityGroupName() {
return securityGroupName;
}
public void setSecurityGroupName(String securityGroupName) {
this.securityGroupName = securityGroupName;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public String getCidr() {
return cidr;
}
public void setCidr(String cidr) {
this.cidr = cidr;
}
}

View File

@ -51,6 +51,9 @@ public class SecurityGroupResponse extends BaseResponse {
@SerializedName("ingressrule") @Param(description="the list of ingress rules associated with the security group", responseObject = IngressRuleResponse.class)
private List<IngressRuleResponse> ingressRules;
@SerializedName("egressrule") @Param(description="the list of ingress rules associated with the security group", responseObject = EgressRuleResponse.class)
private List<EgressRuleResponse> egressRules;
public Long getId() {
return id;
}
@ -102,10 +105,18 @@ public class SecurityGroupResponse extends BaseResponse {
public List<IngressRuleResponse> getIngressRules() {
return ingressRules;
}
public List<EgressRuleResponse> getEgressRules() {
return egressRules;
}
public void setIngressRules(List<IngressRuleResponse> ingressRules) {
this.ingressRules = ingressRules;
}
public void setEgressRules(List<EgressRuleResponse> egressRules) {
this.egressRules = egressRules;
}
@Override
public Long getObjectId() {

View File

@ -0,0 +1,39 @@
/**
* Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
*
* This software is licensed under the GNU General Public License v3 or later.
*
* It is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.cloud.network.security;
import com.cloud.async.AsyncInstanceCreateStatus;
public interface EgressRule {
long getId();
long getSecurityGroupId();
int getStartPort();
int getEndPort();
String getProtocol();
AsyncInstanceCreateStatus getCreateStatus();
Long getAllowedNetworkId();
String getAllowedDestinationIpCidr();
}

View File

@ -20,10 +20,12 @@ package com.cloud.network.security;
import java.util.List;
import com.cloud.api.commands.AuthorizeSecurityGroupIngressCmd;
import com.cloud.api.commands.AuthorizeSecurityGroupEgressCmd;
import com.cloud.api.commands.CreateSecurityGroupCmd;
import com.cloud.api.commands.DeleteSecurityGroupCmd;
import com.cloud.api.commands.ListSecurityGroupsCmd;
import com.cloud.api.commands.RevokeSecurityGroupIngressCmd;
import com.cloud.api.commands.RevokeSecurityGroupEgressCmd;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.exception.ResourceInUseException;
@ -36,6 +38,7 @@ public interface SecurityGroupService {
*/
public SecurityGroup createSecurityGroup(CreateSecurityGroupCmd command) throws PermissionDeniedException, InvalidParameterValueException;
boolean revokeSecurityGroupIngress(RevokeSecurityGroupIngressCmd cmd);
boolean revokeSecurityGroupEgress(RevokeSecurityGroupEgressCmd cmd);
boolean deleteSecurityGroup(DeleteSecurityGroupCmd cmd) throws ResourceInUseException;
@ -47,6 +50,6 @@ public interface SecurityGroupService {
public List<? extends SecurityGroupRules> searchForSecurityGroupRules(ListSecurityGroupsCmd cmd) throws PermissionDeniedException, InvalidParameterValueException;
public List<? extends IngressRule> authorizeSecurityGroupIngress(AuthorizeSecurityGroupIngressCmd cmd);
public List<? extends EgressRule> authorizeSecurityGroupEgress(AuthorizeSecurityGroupEgressCmd cmd);
}

View File

@ -221,6 +221,8 @@ createSecurityGroup=com.cloud.api.commands.CreateSecurityGroupCmd;15
deleteSecurityGroup=com.cloud.api.commands.DeleteSecurityGroupCmd;15
authorizeSecurityGroupIngress=com.cloud.api.commands.AuthorizeSecurityGroupIngressCmd;15
revokeSecurityGroupIngress=com.cloud.api.commands.RevokeSecurityGroupIngressCmd;15
authorizeSecurityGroupEgress=com.cloud.api.commands.AuthorizeSecurityGroupEgressCmd;15
revokeSecurityGroupEgress=com.cloud.api.commands.RevokeSecurityGroupEgressCmd;15
listSecurityGroups=com.cloud.api.commands.ListSecurityGroupsCmd;15
#### vm group commands

View File

@ -114,6 +114,8 @@ import com.cloud.agent.api.RebootCommand;
import com.cloud.agent.api.RebootRouterCommand;
import com.cloud.agent.api.SecurityIngressRuleAnswer;
import com.cloud.agent.api.SecurityIngressRulesCmd;
import com.cloud.agent.api.SecurityEgressRuleAnswer;
import com.cloud.agent.api.SecurityEgressRulesCmd;
import com.cloud.agent.api.SetupAnswer;
import com.cloud.agent.api.SetupCommand;
import com.cloud.agent.api.StartAnswer;
@ -479,6 +481,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
return execute((CheckSshCommand)cmd);
} else if (clazz == SecurityIngressRulesCmd.class) {
return execute((SecurityIngressRulesCmd) cmd);
} else if (clazz == SecurityEgressRulesCmd.class) {
return execute((SecurityEgressRulesCmd) cmd);
} else if (clazz == OvsCreateGreTunnelCommand.class) {
return execute((OvsCreateGreTunnelCommand)cmd);
} else if (clazz == OvsSetTagAndFlowCommand.class) {
@ -4696,6 +4700,36 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
return new OvsCreateGreTunnelAnswer(cmd, false, "EXCEPTION", _host.ip, bridge);
}
private Answer execute(SecurityEgressRulesCmd cmd) {
Connection conn = getConnection();
if (s_logger.isTraceEnabled()) {
s_logger.trace("Sending network rules command to " + _host.ip);
}
if (!_canBridgeFirewall) {
s_logger.info("Host " + _host.ip + " cannot do bridge firewalling");
return new SecurityEgressRuleAnswer(cmd, false, "Host " + _host.ip + " cannot do bridge firewalling");
}
String result = callHostPlugin(conn, "vmops", "network_rules",
"vmName", cmd.getVmName(),
"vmIP", cmd.getGuestIp(),
"vmMAC", cmd.getGuestMac(),
"type", "egress",
"vmID", Long.toString(cmd.getVmId()),
"signature", cmd.getSignature(),
"seqno", Long.toString(cmd.getSeqNum()),
"rules", cmd.stringifyRules());
if (result == null || result.isEmpty() || !Boolean.parseBoolean(result)) {
s_logger.warn("Failed to program network rules for vm " + cmd.getVmName());
return new SecurityEgressRuleAnswer(cmd, false, "programming network rules failed");
} else {
s_logger.info("Programmed network rules for vm " + cmd.getVmName() + " guestIp=" + cmd.getGuestIp() + ", numrules=" + cmd.getRuleSet().length);
return new SecurityEgressRuleAnswer(cmd);
}
}
private Answer execute(SecurityIngressRulesCmd cmd) {
Connection conn = getConnection();
if (s_logger.isTraceEnabled()) {
@ -4711,6 +4745,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
"vmName", cmd.getVmName(),
"vmIP", cmd.getGuestIp(),
"vmMAC", cmd.getGuestMac(),
"type", "ingress",
"vmID", Long.toString(cmd.getVmId()),
"signature", cmd.getSignature(),
"seqno", Long.toString(cmd.getSeqNum()),

View File

@ -0,0 +1,126 @@
/**
* Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
*
* This software is licensed under the GNU General Public License v3 or later.
*
* It is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.cloud.network.security;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import com.cloud.async.AsyncInstanceCreateStatus;
import com.google.gson.annotations.Expose;
@Entity
@Table(name = ("security_egress_rule"))
public class EgressRuleVO implements EgressRule {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id;
@Column(name = "security_group_id")
private long securityGroupId;
@Column(name = "start_port")
private int startPort;
@Column(name = "end_port")
private int endPort;
@Column(name = "protocol")
private String protocol;
@Column(name = "allowed_network_id", nullable = true)
private Long allowedNetworkId = null;
@Column(name = "allowed_ip_cidr", nullable = true)
private String allowedDestinationIpCidr = null;
@Expose
@Column(name = "create_status", updatable = true, nullable = false)
@Enumerated(value = EnumType.STRING)
private AsyncInstanceCreateStatus createStatus;
public EgressRuleVO() {
}
public EgressRuleVO(long securityGroupId, int fromPort, int toPort, String protocol, long allowedNetworkId) {
this.securityGroupId = securityGroupId;
this.startPort = fromPort;
this.endPort = toPort;
this.protocol = protocol;
this.allowedNetworkId = allowedNetworkId;
}
public EgressRuleVO(long securityGroupId, int fromPort, int toPort, String protocol, String allowedIpCidr) {
this.securityGroupId = securityGroupId;
this.startPort = fromPort;
this.endPort = toPort;
this.protocol = protocol;
this.allowedDestinationIpCidr = allowedIpCidr;
}
@Override
public long getId() {
return id;
}
@Override
public long getSecurityGroupId() {
return securityGroupId;
}
@Override
public int getStartPort() {
return startPort;
}
@Override
public int getEndPort() {
return endPort;
}
@Override
public String getProtocol() {
return protocol;
}
@Override
public AsyncInstanceCreateStatus getCreateStatus() {
return createStatus;
}
public void setCreateStatus(AsyncInstanceCreateStatus createStatus) {
this.createStatus = createStatus;
}
@Override
public Long getAllowedNetworkId() {
return allowedNetworkId;
}
@Override
public String getAllowedDestinationIpCidr() {
return allowedDestinationIpCidr;
}
}

View File

@ -1,6 +1,6 @@
#!/usr/bin/python
# Version @VERSION@
#
# Version 2.2.8.2011-08-18T08:15:52Z
#
# A plugin for executing script needed by vmops cloud
import os, sys, time
@ -431,6 +431,7 @@ def ipset(ipsetname, proto, start, end, ips):
def destroy_network_rules_for_vm(session, args):
vm_name = args.pop('vmName')
vmchain = chain_name(vm_name)
vmchain_egress = chain_name(vm_name) + "-egress"
vmchain_default = chain_name_def(vm_name)
delete_rules_for_vm_in_bridge_firewall_chain(vm_name)
@ -450,6 +451,11 @@ def destroy_network_rules_for_vm(session, args):
util.SMlog("Ignoring failure to delete chain " + vmchain)
try:
util.pread2(['iptables', '-F', vmchain_egress])
util.pread2(['iptables', '-X', vmchain_egress])
except:
util.SMlog("Ignoring failure to delete chain " + vmchain_egress)
remove_rule_log_for_vm(vm_name)
@ -623,6 +629,7 @@ def default_network_rules(session, args):
vmchain = chain_name(vm_name)
vmchain_egress = chain_name(vm_name) +"-egress"
vmchain_default = chain_name_def(vm_name)
destroy_ebtables_rules(vmchain)
@ -632,6 +639,11 @@ def default_network_rules(session, args):
util.pread2(['iptables', '-N', vmchain])
except:
util.pread2(['iptables', '-F', vmchain])
try:
util.pread2(['iptables', '-N', vmchain_egress])
except:
util.pread2(['iptables', '-F', vmchain_egress])
try:
util.pread2(['iptables', '-N', vmchain_default])
@ -650,7 +662,7 @@ def default_network_rules(session, args):
#don't let vm spoof its ip address
for v in vifs:
util.pread2(['iptables', '-A', vmchain_default, '-m', 'physdev', '--physdev-is-bridged', '--physdev-in', v, '--source', vm_ip, '-j', 'RETURN'])
util.pread2(['iptables', '-A', vmchain_default, '-m', 'physdev', '--physdev-is-bridged', '--physdev-in', v, '--source', vm_ip, '-j', vmchain_egress])
util.pread2(['iptables', '-A', vmchain_default, '-j', vmchain])
except:
util.SMlog("Failed to program default rules for vm " + vm_name)
@ -944,6 +956,7 @@ def network_rules(session, args):
vm_name = args.get('vmName')
vm_ip = args.get('vmIP')
vm_id = args.get('vmID')
type = args.get('type')
signature = args.pop('signature')
seqno = args.pop('seqno')
try:
@ -968,8 +981,11 @@ def network_rules(session, args):
vifs.append(tap)
except:
pass
vmchain = chain_name(vm_name)
if type == 'egress':
vmchain = chain_name(vm_name) + "-egress"
else:
vmchain = chain_name(vm_name)
changes = check_rule_log_for_vm (vm_name, vm_id, vm_ip, domid, signature, seqno)

View File

@ -50,6 +50,8 @@ import com.cloud.api.response.HostResponse;
import com.cloud.api.response.IPAddressResponse;
import com.cloud.api.response.IngressRuleResponse;
import com.cloud.api.response.IngressRuleResultObject;
import com.cloud.api.response.EgressRuleResponse;
import com.cloud.api.response.EgressRuleResultObject;
import com.cloud.api.response.InstanceGroupResponse;
import com.cloud.api.response.IpForwardingRuleResponse;
import com.cloud.api.response.ListResponse;
@ -115,6 +117,7 @@ import com.cloud.network.rules.LoadBalancer;
import com.cloud.network.rules.PortForwardingRule;
import com.cloud.network.rules.StaticNatRule;
import com.cloud.network.security.IngressRule;
import com.cloud.network.security.EgressRule;
import com.cloud.network.security.SecurityGroup;
import com.cloud.network.security.SecurityGroupRules;
import com.cloud.offering.DiskOffering;
@ -1988,7 +1991,76 @@ public class ApiResponseHelper implements ResponseGenerator {
}
return response;
}
@Override
public SecurityGroupResponse createSecurityGroupResponseFromEgressRule(List<? extends EgressRule> egressRules) {
SecurityGroupResponse response = new SecurityGroupResponse();
Map<Long, Account> securiytGroupAccounts = new HashMap<Long, Account>();
Map<Long, SecurityGroup> allowedSecurityGroups = new HashMap<Long, SecurityGroup>();
Map<Long, Account> allowedSecuriytGroupAccounts = new HashMap<Long, Account>();
if ((egressRules != null) && !egressRules.isEmpty()) {
SecurityGroup securityGroup = ApiDBUtils.findSecurityGroupById(egressRules.get(0).getSecurityGroupId());
response.setId(securityGroup.getId());
response.setName(securityGroup.getName());
response.setDescription(securityGroup.getDescription());
Account account = securiytGroupAccounts.get(securityGroup.getAccountId());
if (account == null) {
account = ApiDBUtils.findAccountById(securityGroup.getAccountId());
securiytGroupAccounts.put(securityGroup.getAccountId(), account);
}
response.setAccountName(account.getAccountName());
response.setDomainId(account.getDomainId());
response.setDomainName(ApiDBUtils.findDomainById(securityGroup.getDomainId()).getName());
List<EgressRuleResponse> responses = new ArrayList<EgressRuleResponse>();
for (EgressRule egressRule : egressRules) {
EgressRuleResponse egressData = new EgressRuleResponse();
egressData.setRuleId(egressRule.getId());
egressData.setProtocol(egressRule.getProtocol());
if ("icmp".equalsIgnoreCase(egressRule.getProtocol())) {
egressData.setIcmpType(egressRule.getStartPort());
egressData.setIcmpCode(egressRule.getEndPort());
} else {
egressData.setStartPort(egressRule.getStartPort());
egressData.setEndPort(egressRule.getEndPort());
}
Long allowedSecurityGroupId = egressRule.getAllowedNetworkId();
if (allowedSecurityGroupId != null) {
SecurityGroup allowedSecurityGroup = allowedSecurityGroups.get(allowedSecurityGroupId);
if (allowedSecurityGroup == null) {
allowedSecurityGroup = ApiDBUtils.findSecurityGroupById(allowedSecurityGroupId);
allowedSecurityGroups.put(allowedSecurityGroupId, allowedSecurityGroup);
}
egressData.setSecurityGroupName(allowedSecurityGroup.getName());
Account allowedAccount = allowedSecuriytGroupAccounts.get(allowedSecurityGroup.getAccountId());
if (allowedAccount == null) {
allowedAccount = ApiDBUtils.findAccountById(allowedSecurityGroup.getAccountId());
allowedSecuriytGroupAccounts.put(allowedAccount.getId(), allowedAccount);
}
egressData.setAccountName(allowedAccount.getAccountName());
} else {
egressData.setCidr(egressRule.getAllowedDestinationIpCidr());
}
egressData.setObjectName("egressrule");
responses.add(egressData);
}
response.setEgressRules(responses);
response.setObjectName("securitygroup");
}
return response;
}
@Override
public NetworkOfferingResponse createNetworkOfferingResponse(NetworkOffering offering) {
NetworkOfferingResponse response = new NetworkOfferingResponse();

View File

@ -0,0 +1,112 @@
/**
* Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
*
* This software is licensed under the GNU General Public License v3 or later.
*
* It is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.cloud.api.response;
import com.cloud.serializer.Param;
public class EgressRuleResultObject {
@Param(name="id")
private Long id;
@Param(name="startport")
private int startPort;
@Param(name="endport")
private int endPort;
@Param(name="protocol")
private String protocol;
@Param(name="securitygroup")
private String allowedSecurityGroup = null;
@Param(name="account")
private String allowedSecGroupAcct = null;
@Param(name="cidr")
private String allowedDestinationIpCidr = null;
public EgressRuleResultObject() { }
public EgressRuleResultObject(Long id, int startPort, int endPort, String protocol, String allowedSecurityGroup, String allowedSecGroupAcct, String allowedSourceIpCidr) {
this.id = id;
this.startPort = startPort;
this.endPort = endPort;
this.protocol = protocol;
this.allowedSecurityGroup = allowedSecurityGroup;
this.allowedSecGroupAcct = allowedSecGroupAcct;
this.allowedDestinationIpCidr = allowedSourceIpCidr;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public int getStartPort() {
return startPort;
}
public void setStartPort(int startPort) {
this.startPort = startPort;
}
public int getEndPort() {
return endPort;
}
public void setEndPort(int endPort) {
this.endPort = endPort;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public String getAllowedSecurityGroup() {
return allowedSecurityGroup;
}
public void setAllowedSecurityGroup(String allowedSecurityGroup) {
this.allowedSecurityGroup = allowedSecurityGroup;
}
public String getAllowedSecGroupAcct() {
return allowedSecGroupAcct;
}
public void setAllowedSecGroupAcct(String allowedSecGroupAcct) {
this.allowedSecGroupAcct = allowedSecGroupAcct;
}
public String getAllowedDestinationIpCidr() {
return allowedDestinationIpCidr;
}
public void setAllowedDestinationIpCidr(String allowedDestinationIpCidr) {
this.allowedDestinationIpCidr = allowedDestinationIpCidr;
}
}

View File

@ -98,6 +98,7 @@ import com.cloud.network.rules.RulesManagerImpl;
import com.cloud.network.rules.dao.PortForwardingRulesDaoImpl;
import com.cloud.network.security.SecurityGroupManagerImpl;
import com.cloud.network.security.dao.IngressRuleDaoImpl;
import com.cloud.network.security.dao.EgressRuleDaoImpl;
import com.cloud.network.security.dao.SecurityGroupDaoImpl;
import com.cloud.network.security.dao.SecurityGroupRulesDaoImpl;
import com.cloud.network.security.dao.SecurityGroupVMMapDaoImpl;
@ -203,6 +204,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com
addDao("DataCenterIpAddressDao", DataCenterIpAddressDaoImpl.class);
addDao("SecurityGroupDao", SecurityGroupDaoImpl.class);
addDao("IngressRuleDao", IngressRuleDaoImpl.class);
addDao("EgressRuleDao", EgressRuleDaoImpl.class);
addDao("SecurityGroupVMMapDao", SecurityGroupVMMapDaoImpl.class);
addDao("SecurityGroupRulesDao", SecurityGroupRulesDaoImpl.class);
addDao("SecurityGroupWorkDao", SecurityGroupWorkDaoImpl.class);

View File

@ -43,12 +43,16 @@ import com.cloud.agent.AgentManager;
import com.cloud.agent.api.NetworkRulesSystemVmCommand;
import com.cloud.agent.api.SecurityIngressRulesCmd;
import com.cloud.agent.api.SecurityIngressRulesCmd.IpPortAndProto;
import com.cloud.agent.api.SecurityEgressRulesCmd;
import com.cloud.agent.api.SecurityEgressRulesCmd.EgressIpPortAndProto;
import com.cloud.agent.manager.Commands;
import com.cloud.api.commands.AuthorizeSecurityGroupIngressCmd;
import com.cloud.api.commands.AuthorizeSecurityGroupEgressCmd;
import com.cloud.api.commands.CreateSecurityGroupCmd;
import com.cloud.api.commands.DeleteSecurityGroupCmd;
import com.cloud.api.commands.ListSecurityGroupsCmd;
import com.cloud.api.commands.RevokeSecurityGroupIngressCmd;
import com.cloud.api.commands.RevokeSecurityGroupEgressCmd;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.domain.Domain;
import com.cloud.domain.DomainVO;
@ -64,6 +68,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.network.NetworkManager;
import com.cloud.network.security.SecurityGroupWorkVO.Step;
import com.cloud.network.security.dao.IngressRuleDao;
import com.cloud.network.security.dao.EgressRuleDao;
import com.cloud.network.security.dao.SecurityGroupDao;
import com.cloud.network.security.dao.SecurityGroupRulesDao;
import com.cloud.network.security.dao.SecurityGroupVMMapDao;
@ -110,6 +115,8 @@ public class SecurityGroupManagerImpl implements SecurityGroupManager, SecurityG
@Inject
IngressRuleDao _ingressRuleDao;
@Inject
EgressRuleDao _egressRuleDao;
@Inject
SecurityGroupVMMapDao _securityGroupVMMapDao;
@Inject
SecurityGroupRulesDao _securityGroupRulesDao;
@ -281,8 +288,41 @@ public class SecurityGroupManagerImpl implements SecurityGroupManager, SecurityG
}
}
protected Map<PortAndProto, Set<String>> generateEgressRulesForVM(Long userVmId) {
protected Map<PortAndProto, Set<String>> generateRulesForVM(Long userVmId) {
Map<PortAndProto, Set<String>> allowed = new TreeMap<PortAndProto, Set<String>>();
List<SecurityGroupVMMapVO> groupsForVm = _securityGroupVMMapDao.listByInstanceId(userVmId);
for (SecurityGroupVMMapVO mapVO : groupsForVm) {
List<EgressRuleVO> rules = _egressRuleDao.listBySecurityGroupId(mapVO.getSecurityGroupId());
for (EgressRuleVO rule : rules) {
PortAndProto portAndProto = new PortAndProto(rule.getProtocol(), rule.getStartPort(), rule.getEndPort());
Set<String> cidrs = allowed.get(portAndProto);
if (cidrs == null) {
cidrs = new TreeSet<String>(new CidrComparator());
}
if (rule.getAllowedNetworkId() != null) {
List<SecurityGroupVMMapVO> allowedInstances = _securityGroupVMMapDao.listBySecurityGroup(rule.getAllowedNetworkId(), State.Running);
for (SecurityGroupVMMapVO ngmapVO : allowedInstances) {
Nic defaultNic = _networkMgr.getDefaultNic(ngmapVO.getInstanceId());
if (defaultNic != null) {
String cidr = defaultNic.getIp4Address();
cidr = cidr + "/32";
cidrs.add(cidr);
}
}
} else if (rule.getAllowedDestinationIpCidr() != null) {
cidrs.add(rule.getAllowedDestinationIpCidr());
}
if (cidrs.size() > 0) {
allowed.put(portAndProto, cidrs);
}
}
}
return allowed;
}
protected Map<PortAndProto, Set<String>> generateIngressRulesForVM(Long userVmId) {
Map<PortAndProto, Set<String>> allowed = new TreeMap<PortAndProto, Set<String>>();
@ -422,7 +462,7 @@ public class SecurityGroupManagerImpl implements SecurityGroupManager, SecurityG
return affectedVms;
}
protected SecurityIngressRulesCmd generateRulesetCmd(String vmName, String guestIp, String guestMac, Long vmId, String signature, long seqnum, Map<PortAndProto, Set<String>> rules) {
protected SecurityIngressRulesCmd generateIngressRulesetCmd(String vmName, String guestIp, String guestMac, Long vmId, String signature, long seqnum, Map<PortAndProto, Set<String>> rules) {
List<IpPortAndProto> result = new ArrayList<IpPortAndProto>();
for (PortAndProto pAp : rules.keySet()) {
Set<String> cidrs = rules.get(pAp);
@ -433,7 +473,19 @@ public class SecurityGroupManagerImpl implements SecurityGroupManager, SecurityG
}
return new SecurityIngressRulesCmd(guestIp, guestMac, vmName, vmId, signature, seqnum, result.toArray(new IpPortAndProto[result.size()]));
}
protected SecurityEgressRulesCmd generateEgressRulesetCmd(String vmName, String guestIp, String guestMac, Long vmId, String signature, long seqnum, Map<PortAndProto, Set<String>> rules) {
List<EgressIpPortAndProto> result = new ArrayList<EgressIpPortAndProto>();
for (PortAndProto pAp : rules.keySet()) {
Set<String> cidrs = rules.get(pAp);
if (cidrs.size() > 0) {
EgressIpPortAndProto ipPortAndProto = new SecurityEgressRulesCmd.EgressIpPortAndProto(pAp.getProto(), pAp.getStartPort(), pAp.getEndPort(), cidrs.toArray(new String[cidrs.size()]));
result.add(ipPortAndProto);
}
}
return new SecurityEgressRulesCmd(guestIp, guestMac, vmName, vmId, signature, seqnum, result.toArray(new EgressIpPortAndProto[result.size()]));
}
protected void handleVmStopped(VMInstanceVO vm) {
if (vm.getType() != VirtualMachine.Type.User || !isVmSecurityGroupEnabled(vm.getId()))
return;
@ -689,7 +741,232 @@ public class SecurityGroupManagerImpl implements SecurityGroupManager, SecurityG
}
}
@Override
@DB
@SuppressWarnings("rawtypes")
public List<EgressRuleVO> authorizeSecurityGroupEgress(AuthorizeSecurityGroupEgressCmd cmd) {
Long securityGroupId = cmd.getSecurityGroupId();
String protocol = cmd.getProtocol();
Integer startPort = cmd.getStartPort();
Integer endPort = cmd.getEndPort();
Integer icmpType = cmd.getIcmpType();
Integer icmpCode = cmd.getIcmpCode();
List<String> cidrList = cmd.getCidrList();
Map groupList = cmd.getUserSecurityGroupList();
Integer startPortOrType = null;
Integer endPortOrCode = null;
// Validate parameters
SecurityGroup securityGroup = _securityGroupDao.findById(securityGroupId);
if (securityGroup == null) {
throw new InvalidParameterValueException("Unable to find security group by id " + securityGroupId);
}
if (cidrList == null && groupList == null) {
throw new InvalidParameterValueException("At least one cidr or at least one security group needs to be specified");
}
Account caller = UserContext.current().getCaller();
Account owner = _accountMgr.getAccount(securityGroup.getAccountId());
if (owner == null) {
throw new InvalidParameterValueException("Unable to find security group owner by id=" + securityGroup.getAccountId());
}
// Verify permissions
_accountMgr.checkAccess(caller, securityGroup);
Long domainId = owner.getDomainId();
if (protocol == null) {
protocol = NetUtils.ALL_PROTO;
}
if (!NetUtils.isValidSecurityGroupProto(protocol)) {
throw new InvalidParameterValueException("Invalid protocol " + protocol);
}
if ("icmp".equalsIgnoreCase(protocol)) {
if ((icmpType == null) || (icmpCode == null)) {
throw new InvalidParameterValueException("Invalid ICMP type/code specified, icmpType = " + icmpType + ", icmpCode = " + icmpCode);
}
if (icmpType == -1 && icmpCode != -1) {
throw new InvalidParameterValueException("Invalid icmp type range");
}
if (icmpCode > 255) {
throw new InvalidParameterValueException("Invalid icmp code ");
}
startPortOrType = icmpType;
endPortOrCode = icmpCode;
} else if (protocol.equals(NetUtils.ALL_PROTO)) {
if ((startPort != null) || (endPort != null)) {
throw new InvalidParameterValueException("Cannot specify startPort or endPort without specifying protocol");
}
startPortOrType = 0;
endPortOrCode = 0;
} else {
if ((startPort == null) || (endPort == null)) {
throw new InvalidParameterValueException("Invalid port range specified, startPort = " + startPort + ", endPort = " + endPort);
}
if (startPort == 0 && endPort == 0) {
endPort = 65535;
}
if (startPort > endPort) {
throw new InvalidParameterValueException("Invalid port range " + startPort + ":" + endPort);
}
if (startPort > 65535 || endPort > 65535 || startPort < -1 || endPort < -1) {
throw new InvalidParameterValueException("Invalid port numbers " + startPort + ":" + endPort);
}
if (startPort < 0 || endPort < 0) {
throw new InvalidParameterValueException("Invalid port range " + startPort + ":" + endPort);
}
startPortOrType = startPort;
endPortOrCode = endPort;
}
protocol = protocol.toLowerCase();
List<SecurityGroupVO> authorizedGroups = new ArrayList<SecurityGroupVO>();
if (groupList != null) {
Collection userGroupCollection = groupList.values();
Iterator iter = userGroupCollection.iterator();
while (iter.hasNext()) {
HashMap userGroup = (HashMap) iter.next();
String group = (String) userGroup.get("group");
String authorizedAccountName = (String) userGroup.get("account");
if ((group == null) || (authorizedAccountName == null)) {
throw new InvalidParameterValueException(
"Invalid user group specified, fields 'group' and 'account' cannot be null, please specify groups in the form: userGroupList[0].group=XXX&userGroupList[0].account=YYY");
}
Account authorizedAccount = _accountDao.findActiveAccount(authorizedAccountName, domainId);
if (authorizedAccount == null) {
throw new InvalidParameterValueException("Nonexistent account: " + authorizedAccountName + " when trying to authorize ingress for " + securityGroupId + ":" + protocol + ":"
+ startPortOrType + ":" + endPortOrCode);
}
SecurityGroupVO groupVO = _securityGroupDao.findByAccountAndName(authorizedAccount.getId(), group);
if (groupVO == null) {
throw new InvalidParameterValueException("Nonexistent group " + group + " for account " + authorizedAccountName + "/" + domainId + " is given, unable to authorize ingress.");
}
// Check permissions
_accountMgr.checkAccess(caller, groupVO);
authorizedGroups.add(groupVO);
}
}
final Transaction txn = Transaction.currentTxn();
final Set<SecurityGroupVO> authorizedGroups2 = new TreeSet<SecurityGroupVO>(new SecurityGroupVOComparator());
authorizedGroups2.addAll(authorizedGroups); // Ensure we don't re-lock the same row
txn.start();
// Prevents other threads/management servers from creating duplicate ingress rules
securityGroup = _securityGroupDao.acquireInLockTable(securityGroupId);
if (securityGroup == null) {
s_logger.warn("Could not acquire lock on network security group: id= " + securityGroupId);
return null;
}
List<EgressRuleVO> newRules = new ArrayList<EgressRuleVO>();
try {
for (final SecurityGroupVO ngVO : authorizedGroups2) {
final Long ngId = ngVO.getId();
// Don't delete the referenced group from under us
if (ngVO.getId() != securityGroup.getId()) {
final SecurityGroupVO tmpGrp = _securityGroupDao.lockRow(ngId, false);
if (tmpGrp == null) {
s_logger.warn("Failed to acquire lock on security group: " + ngId);
txn.rollback();
return null;
}
}
EgressRuleVO egressRule = _egressRuleDao.findByProtoPortsAndAllowedGroupId(securityGroup.getId(), protocol, startPortOrType, endPortOrCode, ngVO.getId());
if (egressRule != null) {
continue; // rule already exists.
}
egressRule = new EgressRuleVO(securityGroup.getId(), startPortOrType, endPortOrCode, protocol, ngVO.getId());
egressRule = _egressRuleDao.persist(egressRule);
newRules.add(egressRule);
}
if (cidrList != null) {
for (String cidr : cidrList) {
EgressRuleVO egressRule = _egressRuleDao.findByProtoPortsAndCidr(securityGroup.getId(), protocol, startPortOrType, endPortOrCode, cidr);
if (egressRule != null) {
continue;
}
egressRule = new EgressRuleVO(securityGroup.getId(), startPortOrType, endPortOrCode, protocol, cidr);
egressRule = _egressRuleDao.persist(egressRule);
newRules.add(egressRule);
}
}
if (s_logger.isDebugEnabled()) {
s_logger.debug("Added " + newRules.size() + " rules to security group " + securityGroup.getName());
}
txn.commit();
final Set<Long> affectedVms = new HashSet<Long>();
affectedVms.addAll(_securityGroupVMMapDao.listVmIdsBySecurityGroup(securityGroup.getId()));
scheduleRulesetUpdateToHosts(affectedVms, true, null);
return newRules;
} catch (Exception e) {
s_logger.warn("Exception caught when adding ingress rules ", e);
throw new CloudRuntimeException("Exception caught when adding ingress rules", e);
} finally {
if (securityGroup != null) {
_securityGroupDao.releaseFromLockTable(securityGroup.getId());
}
}
}
@Override
@DB
public boolean revokeSecurityGroupEgress(RevokeSecurityGroupEgressCmd cmd) {
// input validation
Account caller = UserContext.current().getCaller();
Long id = cmd.getId();
IngressRuleVO rule = _ingressRuleDao.findById(id);
if (rule == null) {
s_logger.debug("Unable to find ingress rule with id " + id);
throw new InvalidParameterValueException("Unable to find ingress rule with id " + id);
}
// Check permissions
SecurityGroup securityGroup = _securityGroupDao.findById(rule.getSecurityGroupId());
_accountMgr.checkAccess(caller, securityGroup);
SecurityGroupVO groupHandle = null;
final Transaction txn = Transaction.currentTxn();
try {
txn.start();
// acquire lock on parent group (preserving this logic)
groupHandle = _securityGroupDao.acquireInLockTable(rule.getSecurityGroupId());
if (groupHandle == null) {
s_logger.warn("Could not acquire lock on security group id: " + rule.getSecurityGroupId());
return false;
}
_ingressRuleDao.remove(id);
s_logger.debug("revokeSecurityGroupIngress succeeded for ingress rule id: " + id);
final Set<Long> affectedVms = new HashSet<Long>();
affectedVms.addAll(_securityGroupVMMapDao.listVmIdsBySecurityGroup(groupHandle.getId()));
scheduleRulesetUpdateToHosts(affectedVms, true, null);
return true;
} catch (Exception e) {
s_logger.warn("Exception caught when deleting ingress rules ", e);
throw new CloudRuntimeException("Exception caught when deleting ingress rules", e);
} finally {
if (groupHandle != null) {
_securityGroupDao.releaseFromLockTable(groupHandle.getId());
}
txn.commit();
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_SECURITY_GROUP_CREATE, eventDescription = "creating security group")
public SecurityGroupVO createSecurityGroup(CreateSecurityGroupCmd cmd) throws PermissionDeniedException, InvalidParameterValueException {
@ -791,12 +1068,23 @@ public class SecurityGroupManagerImpl implements SecurityGroupManager, SecurityG
seqnum = log.getLogsequence();
if (vm != null && vm.getState() == State.Running) {
Map<PortAndProto, Set<String>> rules = generateRulesForVM(userVmId);
Map<PortAndProto, Set<String>> ingressRules = generateIngressRulesForVM(userVmId);
Map<PortAndProto, Set<String>> egressRules = generateEgressRulesForVM(userVmId);
agentId = vm.getHostId();
if (agentId != null) {
_rulesetLogDao.findByVmId(work.getInstanceId());
SecurityIngressRulesCmd cmd = generateRulesetCmd(vm.getInstanceName(), vm.getPrivateIpAddress(), vm.getPrivateMacAddress(), vm.getId(), generateRulesetSignature(rules), seqnum,
rules);
SecurityIngressRulesCmd ingressCmd = generateIngressRulesetCmd(vm.getInstanceName(), vm.getPrivateIpAddress(), vm.getPrivateMacAddress(), vm.getId(), generateRulesetSignature(ingressRules), seqnum,
ingressRules);
Commands ingressCmds = new Commands(ingressCmd);
try {
_agentMgr.send(agentId, ingressCmds, _answerListener);
} catch (AgentUnavailableException e) {
s_logger.debug("Unable to send updates for vm: " + userVmId + "(agentid=" + agentId + ")");
_workDao.updateStep(work.getInstanceId(), seqnum, Step.Done);
}
SecurityEgressRulesCmd cmd = generateEgressRulesetCmd(vm.getInstanceName(), vm.getPrivateIpAddress(), vm.getPrivateMacAddress(), vm.getId(), generateRulesetSignature(egressRules), seqnum,
egressRules);
Commands cmds = new Commands(cmd);
try {
_agentMgr.send(agentId, cmds, _answerListener);

View File

@ -0,0 +1,36 @@
/**
* Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
*
* This software is licensed under the GNU General Public License v3 or later.
*
* It is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.cloud.network.security.dao;
import java.util.List;
import com.cloud.network.security.EgressRuleVO;
import com.cloud.utils.db.GenericDao;
public interface EgressRuleDao extends GenericDao<EgressRuleVO, Long> {
List<EgressRuleVO> listBySecurityGroupId(long networkGroupId);
List<EgressRuleVO> listByAllowedSecurityGroupId(long networkGroupId);
EgressRuleVO findByProtoPortsAndCidr(long networkGroupId, String proto, int startPort, int endPort, String cidr);
EgressRuleVO findByProtoPortsAndGroup(String proto, int startPort, int endPort, String networkGroup);
EgressRuleVO findByProtoPortsAndAllowedGroupId(long networkGroupId, String proto, int startPort, int endPort, Long allowedGroupId);
int deleteBySecurityGroup(long securityGroupId);
int deleteByPortProtoAndGroup(long securityGroupId, String protocol, int startPort,int endPort, Long id);
int deleteByPortProtoAndCidr(long securityGroupId, String protocol, int startPort,int endPort, String cidr);
}

View File

@ -0,0 +1,167 @@
/**
* Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
*
* This software is licensed under the GNU General Public License v3 or later.
*
* It is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.cloud.network.security.dao;
import java.util.List;
import java.util.Map;
import javax.ejb.Local;
import javax.naming.ConfigurationException;
import com.cloud.network.security.EgressRuleVO;
import com.cloud.network.security.SecurityGroupVO;
import com.cloud.utils.component.Inject;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.JoinBuilder;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
@Local(value={EgressRuleDao.class})
public class EgressRuleDaoImpl extends GenericDaoBase<EgressRuleVO, Long> implements EgressRuleDao {
@Inject SecurityGroupDao _securityGroupDao;
protected SearchBuilder<EgressRuleVO> securityGroupIdSearch;
protected SearchBuilder<EgressRuleVO> allowedSecurityGroupIdSearch;
protected SearchBuilder<EgressRuleVO> protoPortsAndCidrSearch;
protected SearchBuilder<EgressRuleVO> protoPortsAndSecurityGroupNameSearch;
protected SearchBuilder<EgressRuleVO> protoPortsAndSecurityGroupIdSearch;
protected EgressRuleDaoImpl() {
securityGroupIdSearch = createSearchBuilder();
securityGroupIdSearch.and("securityGroupId", securityGroupIdSearch.entity().getSecurityGroupId(), SearchCriteria.Op.EQ);
securityGroupIdSearch.done();
allowedSecurityGroupIdSearch = createSearchBuilder();
allowedSecurityGroupIdSearch.and("allowedNetworkId", allowedSecurityGroupIdSearch.entity().getAllowedNetworkId(), SearchCriteria.Op.EQ);
allowedSecurityGroupIdSearch.done();
protoPortsAndCidrSearch = createSearchBuilder();
protoPortsAndCidrSearch.and("securityGroupId", protoPortsAndCidrSearch.entity().getSecurityGroupId(), SearchCriteria.Op.EQ);
protoPortsAndCidrSearch.and("proto", protoPortsAndCidrSearch.entity().getProtocol(), SearchCriteria.Op.EQ);
protoPortsAndCidrSearch.and("startPort", protoPortsAndCidrSearch.entity().getStartPort(), SearchCriteria.Op.EQ);
protoPortsAndCidrSearch.and("endPort", protoPortsAndCidrSearch.entity().getEndPort(), SearchCriteria.Op.EQ);
protoPortsAndCidrSearch.and("cidr", protoPortsAndCidrSearch.entity().getAllowedDestinationIpCidr(), SearchCriteria.Op.EQ);
protoPortsAndCidrSearch.done();
protoPortsAndSecurityGroupIdSearch = createSearchBuilder();
protoPortsAndSecurityGroupIdSearch.and("securityGroupId", protoPortsAndSecurityGroupIdSearch.entity().getSecurityGroupId(), SearchCriteria.Op.EQ);
protoPortsAndSecurityGroupIdSearch.and("proto", protoPortsAndSecurityGroupIdSearch.entity().getProtocol(), SearchCriteria.Op.EQ);
protoPortsAndSecurityGroupIdSearch.and("startPort", protoPortsAndSecurityGroupIdSearch.entity().getStartPort(), SearchCriteria.Op.EQ);
protoPortsAndSecurityGroupIdSearch.and("endPort", protoPortsAndSecurityGroupIdSearch.entity().getEndPort(), SearchCriteria.Op.EQ);
protoPortsAndSecurityGroupIdSearch.and("allowedNetworkId", protoPortsAndSecurityGroupIdSearch.entity().getAllowedNetworkId(), SearchCriteria.Op.EQ);
}
public List<EgressRuleVO> listBySecurityGroupId(long securityGroupId) {
SearchCriteria<EgressRuleVO> sc = securityGroupIdSearch.create();
sc.setParameters("securityGroupId", securityGroupId);
return listBy(sc);
}
public int deleteBySecurityGroup(long securityGroupId) {
SearchCriteria<EgressRuleVO> sc = securityGroupIdSearch.create();
sc.setParameters("securityGroupId", securityGroupId);
return expunge(sc);
}
@Override
public List<EgressRuleVO> listByAllowedSecurityGroupId(long securityGroupId) {
SearchCriteria<EgressRuleVO> sc = allowedSecurityGroupIdSearch.create();
sc.setParameters("allowedNetworkId", securityGroupId);
return listBy(sc);
}
@Override
public EgressRuleVO findByProtoPortsAndCidr(long securityGroupId, String proto, int startPort,
int endPort, String cidr) {
SearchCriteria<EgressRuleVO> sc = protoPortsAndCidrSearch.create();
sc.setParameters("securityGroupId", securityGroupId);
sc.setParameters("proto", proto);
sc.setParameters("startPort", startPort);
sc.setParameters("endPort", endPort);
sc.setParameters("cidr", cidr);
return findOneIncludingRemovedBy(sc);
}
@Override
public EgressRuleVO findByProtoPortsAndGroup(String proto, int startPort,
int endPort, String securityGroup) {
SearchCriteria<EgressRuleVO> sc = protoPortsAndSecurityGroupNameSearch.create();
sc.setParameters("proto", proto);
sc.setParameters("startPort", startPort);
sc.setParameters("endPort", endPort);
sc.setJoinParameters("groupName", "groupName", securityGroup);
return findOneIncludingRemovedBy(sc);
}
@Override
public boolean configure(String name, Map<String, Object> params)
throws ConfigurationException {
protoPortsAndSecurityGroupNameSearch = createSearchBuilder();
protoPortsAndSecurityGroupNameSearch.and("proto", protoPortsAndSecurityGroupNameSearch.entity().getProtocol(), SearchCriteria.Op.EQ);
protoPortsAndSecurityGroupNameSearch.and("startPort", protoPortsAndSecurityGroupNameSearch.entity().getStartPort(), SearchCriteria.Op.EQ);
protoPortsAndSecurityGroupNameSearch.and("endPort", protoPortsAndSecurityGroupNameSearch.entity().getEndPort(), SearchCriteria.Op.EQ);
SearchBuilder<SecurityGroupVO> ngSb = _securityGroupDao.createSearchBuilder();
ngSb.and("groupName", ngSb.entity().getName(), SearchCriteria.Op.EQ);
protoPortsAndSecurityGroupNameSearch.join("groupName", ngSb, protoPortsAndSecurityGroupNameSearch.entity().getAllowedNetworkId(), ngSb.entity().getId(), JoinBuilder.JoinType.INNER);
protoPortsAndSecurityGroupNameSearch.done();
return super.configure(name, params);
}
@Override
public int deleteByPortProtoAndGroup(long securityGroupId, String protocol, int startPort, int endPort, Long allowedGroupId) {
SearchCriteria<EgressRuleVO> sc = protoPortsAndSecurityGroupIdSearch.create();
sc.setParameters("securityGroupId", securityGroupId);
sc.setParameters("proto", protocol);
sc.setParameters("startPort", startPort);
sc.setParameters("endPort", endPort);
sc.setParameters("allowedNetworkId", allowedGroupId);
return expunge(sc);
}
@Override
public int deleteByPortProtoAndCidr(long securityGroupId, String protocol, int startPort, int endPort, String cidr) {
SearchCriteria<EgressRuleVO> sc = protoPortsAndCidrSearch.create();
sc.setParameters("securityGroupId", securityGroupId);
sc.setParameters("proto", protocol);
sc.setParameters("startPort", startPort);
sc.setParameters("endPort", endPort);
sc.setParameters("cidr", cidr);
return expunge(sc);
}
@Override
public EgressRuleVO findByProtoPortsAndAllowedGroupId(long securityGroupId, String proto,
int startPort, int endPort, Long allowedGroupId) {
SearchCriteria<EgressRuleVO> sc = protoPortsAndSecurityGroupIdSearch.create();
sc.addAnd("securityGroupId", SearchCriteria.Op.EQ, securityGroupId);
sc.setParameters("proto", proto);
sc.setParameters("startPort", startPort);
sc.setParameters("endPort", endPort);
sc.setParameters("allowedNetworkId", allowedGroupId);
return findOneIncludingRemovedBy(sc);
}
}

View File

@ -108,6 +108,7 @@ DROP TABLE IF EXISTS `cloud`.`ovs_work`;
DROP TABLE IF EXISTS `cloud`.`remote_access_vpn`;
DROP TABLE IF EXISTS `cloud`.`resource_count`;
DROP TABLE IF EXISTS `cloud`.`security_ingress_rule`;
DROP TABLE IF EXISTS `cloud`.`security_egress_rule`;
DROP TABLE IF EXISTS `cloud`.`stack_maid`;
DROP TABLE IF EXISTS `cloud`.`storage_pool_work`;
DROP TABLE IF EXISTS `cloud`.`user_vm_details`;
@ -1401,6 +1402,18 @@ CREATE TABLE `cloud`.`security_ingress_rule` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `cloud`.`security_egress_rule` (
`id` bigint unsigned NOT NULL auto_increment,
`security_group_id` bigint unsigned NOT NULL,
`start_port` varchar(10) default NULL,
`end_port` varchar(10) default NULL,
`protocol` varchar(16) NOT NULL default 'TCP',
`allowed_network_id` bigint unsigned,
`allowed_ip_cidr` varchar(44),
`create_status` varchar(32) COMMENT 'rule creation status',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `cloud`.`security_group_vm_map` (
`id` bigint unsigned NOT NULL auto_increment,
`security_group_id` bigint unsigned NOT NULL,