diff --git a/api/src/com/cloud/agent/api/SecurityEgressRuleAnswer.java b/api/src/com/cloud/agent/api/SecurityEgressRuleAnswer.java
new file mode 100644
index 00000000000..1b0d33326c1
--- /dev/null
+++ b/api/src/com/cloud/agent/api/SecurityEgressRuleAnswer.java
@@ -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 .
+ *
+ */
+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;
+ }
+
+}
diff --git a/api/src/com/cloud/agent/api/SecurityEgressRulesCmd.java b/api/src/com/cloud/agent/api/SecurityEgressRulesCmd.java
new file mode 100644
index 00000000000..f9c096d7768
--- /dev/null
+++ b/api/src/com/cloud/agent/api/SecurityEgressRulesCmd.java
@@ -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 .
+ *
+ */
+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;
+ }
+
+}
diff --git a/api/src/com/cloud/api/ResponseGenerator.java b/api/src/com/cloud/api/ResponseGenerator.java
index 3122ed217f7..99bdca3eb22 100755
--- a/api/src/com/cloud/api/ResponseGenerator.java
+++ b/api/src/com/cloud/api/ResponseGenerator.java
@@ -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);
diff --git a/api/src/com/cloud/api/commands/AuthorizeSecurityGroupEgressCmd.java b/api/src/com/cloud/api/commands/AuthorizeSecurityGroupEgressCmd.java
new file mode 100644
index 00000000000..e7c89e6e827
--- /dev/null
+++ b/api/src/com/cloud/api/commands/AuthorizeSecurityGroupEgressCmd.java
@@ -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 .
+ *
+ */
+
+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("");
+ }
+
+ 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();
+ }
+}
diff --git a/api/src/com/cloud/api/commands/RevokeSecurityGroupEgressCmd.java b/api/src/com/cloud/api/commands/RevokeSecurityGroupEgressCmd.java
new file mode 100644
index 00000000000..bfddc9ebc5b
--- /dev/null
+++ b/api/src/com/cloud/api/commands/RevokeSecurityGroupEgressCmd.java
@@ -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 .
+ *
+ */
+
+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();
+ }
+}
diff --git a/api/src/com/cloud/api/response/EgressRuleResponse.java b/api/src/com/cloud/api/response/EgressRuleResponse.java
new file mode 100644
index 00000000000..ca3b9fba49d
--- /dev/null
+++ b/api/src/com/cloud/api/response/EgressRuleResponse.java
@@ -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 .
+ *
+ */
+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;
+ }
+}
diff --git a/api/src/com/cloud/api/response/SecurityGroupResponse.java b/api/src/com/cloud/api/response/SecurityGroupResponse.java
index 48f28fc9b99..b4d67466a64 100644
--- a/api/src/com/cloud/api/response/SecurityGroupResponse.java
+++ b/api/src/com/cloud/api/response/SecurityGroupResponse.java
@@ -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 ingressRules;
+ @SerializedName("egressrule") @Param(description="the list of ingress rules associated with the security group", responseObject = EgressRuleResponse.class)
+ private List egressRules;
+
public Long getId() {
return id;
}
@@ -102,10 +105,18 @@ public class SecurityGroupResponse extends BaseResponse {
public List getIngressRules() {
return ingressRules;
}
+
+ public List getEgressRules() {
+ return egressRules;
+ }
public void setIngressRules(List ingressRules) {
this.ingressRules = ingressRules;
}
+
+ public void setEgressRules(List egressRules) {
+ this.egressRules = egressRules;
+ }
@Override
public Long getObjectId() {
diff --git a/api/src/com/cloud/network/security/EgressRule.java b/api/src/com/cloud/network/security/EgressRule.java
new file mode 100644
index 00000000000..005e91d61d5
--- /dev/null
+++ b/api/src/com/cloud/network/security/EgressRule.java
@@ -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 .
+ *
+ */
+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();
+
+}
diff --git a/api/src/com/cloud/network/security/SecurityGroupService.java b/api/src/com/cloud/network/security/SecurityGroupService.java
index a25ba39c423..1642a2e57dc 100644
--- a/api/src/com/cloud/network/security/SecurityGroupService.java
+++ b/api/src/com/cloud/network/security/SecurityGroupService.java
@@ -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);
}
diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in
index 67a1af26975..8510f3e804a 100755
--- a/client/tomcatconf/commands.properties.in
+++ b/client/tomcatconf/commands.properties.in
@@ -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
diff --git a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
index 74b494ce947..6a8e9ff519b 100644
--- a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
+++ b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
@@ -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()),
diff --git a/core/src/com/cloud/network/security/EgressRuleVO.java b/core/src/com/cloud/network/security/EgressRuleVO.java
new file mode 100644
index 00000000000..01d7791ecb1
--- /dev/null
+++ b/core/src/com/cloud/network/security/EgressRuleVO.java
@@ -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 .
+ *
+ */
+
+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;
+ }
+}
diff --git a/scripts/vm/hypervisor/xenserver/vmops b/scripts/vm/hypervisor/xenserver/vmops
index 2e3929290ee..affbd932e27 100755
--- a/scripts/vm/hypervisor/xenserver/vmops
+++ b/scripts/vm/hypervisor/xenserver/vmops
@@ -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)
diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java
index cb7a3a55219..7cc12d90e6e 100755
--- a/server/src/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/com/cloud/api/ApiResponseHelper.java
@@ -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 securiytGroupAccounts = new HashMap();
+ Map allowedSecurityGroups = new HashMap();
+ Map allowedSecuriytGroupAccounts = new HashMap();
+ 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 responses = new ArrayList();
+ 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();
diff --git a/server/src/com/cloud/api/response/EgressRuleResultObject.java b/server/src/com/cloud/api/response/EgressRuleResultObject.java
new file mode 100644
index 00000000000..85313c7cad2
--- /dev/null
+++ b/server/src/com/cloud/api/response/EgressRuleResultObject.java
@@ -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 .
+ *
+ */
+
+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;
+ }
+}
diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java
index 92e71d8ed9f..903a7523480 100755
--- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java
+++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java
@@ -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);
diff --git a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java
index 0706b6f6acf..b9e752d08b2 100755
--- a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java
+++ b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java
@@ -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> generateEgressRulesForVM(Long userVmId) {
- protected Map> generateRulesForVM(Long userVmId) {
+ Map> allowed = new TreeMap>();
+
+ List groupsForVm = _securityGroupVMMapDao.listByInstanceId(userVmId);
+ for (SecurityGroupVMMapVO mapVO : groupsForVm) {
+ List rules = _egressRuleDao.listBySecurityGroupId(mapVO.getSecurityGroupId());
+ for (EgressRuleVO rule : rules) {
+ PortAndProto portAndProto = new PortAndProto(rule.getProtocol(), rule.getStartPort(), rule.getEndPort());
+ Set cidrs = allowed.get(portAndProto);
+ if (cidrs == null) {
+ cidrs = new TreeSet(new CidrComparator());
+ }
+ if (rule.getAllowedNetworkId() != null) {
+ List 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> generateIngressRulesForVM(Long userVmId) {
Map> allowed = new TreeMap>();
@@ -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> rules) {
+ protected SecurityIngressRulesCmd generateIngressRulesetCmd(String vmName, String guestIp, String guestMac, Long vmId, String signature, long seqnum, Map> rules) {
List result = new ArrayList();
for (PortAndProto pAp : rules.keySet()) {
Set 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> rules) {
+ List result = new ArrayList();
+ for (PortAndProto pAp : rules.keySet()) {
+ Set 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 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 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 authorizedGroups = new ArrayList();
+ 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 authorizedGroups2 = new TreeSet(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 newRules = new ArrayList();
+ 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 affectedVms = new HashSet();
+ 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 affectedVms = new HashSet();
+ 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> rules = generateRulesForVM(userVmId);
+ Map> ingressRules = generateIngressRulesForVM(userVmId);
+ Map> 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);
diff --git a/server/src/com/cloud/network/security/dao/EgressRuleDao.java b/server/src/com/cloud/network/security/dao/EgressRuleDao.java
new file mode 100644
index 00000000000..9cc514d0a86
--- /dev/null
+++ b/server/src/com/cloud/network/security/dao/EgressRuleDao.java
@@ -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 .
+ *
+ */
+
+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 {
+ List listBySecurityGroupId(long networkGroupId);
+ List 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);
+
+}
diff --git a/server/src/com/cloud/network/security/dao/EgressRuleDaoImpl.java b/server/src/com/cloud/network/security/dao/EgressRuleDaoImpl.java
new file mode 100644
index 00000000000..adb11b71e43
--- /dev/null
+++ b/server/src/com/cloud/network/security/dao/EgressRuleDaoImpl.java
@@ -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 .
+ *
+ */
+
+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 implements EgressRuleDao {
+
+ @Inject SecurityGroupDao _securityGroupDao;
+
+ protected SearchBuilder securityGroupIdSearch;
+ protected SearchBuilder allowedSecurityGroupIdSearch;
+ protected SearchBuilder protoPortsAndCidrSearch;
+ protected SearchBuilder protoPortsAndSecurityGroupNameSearch;
+ protected SearchBuilder 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 listBySecurityGroupId(long securityGroupId) {
+ SearchCriteria sc = securityGroupIdSearch.create();
+ sc.setParameters("securityGroupId", securityGroupId);
+ return listBy(sc);
+ }
+
+ public int deleteBySecurityGroup(long securityGroupId) {
+ SearchCriteria sc = securityGroupIdSearch.create();
+ sc.setParameters("securityGroupId", securityGroupId);
+ return expunge(sc);
+ }
+
+ @Override
+ public List listByAllowedSecurityGroupId(long securityGroupId) {
+ SearchCriteria 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 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 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 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 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 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 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 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);
+ }
+}
diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql
index 90195382a4f..2d88b5d7449 100755
--- a/setup/db/create-schema.sql
+++ b/setup/db/create-schema.sql
@@ -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,