diff --git a/api/src/com/cloud/agent/api/routing/SetFirewallRulesAnswer.java b/api/src/com/cloud/agent/api/routing/SetFirewallRulesAnswer.java index 701b5f5aeb8..92f6fa60ed5 100644 --- a/api/src/com/cloud/agent/api/routing/SetFirewallRulesAnswer.java +++ b/api/src/com/cloud/agent/api/routing/SetFirewallRulesAnswer.java @@ -25,9 +25,8 @@ public class SetFirewallRulesAnswer extends Answer { protected SetFirewallRulesAnswer() { } - public SetFirewallRulesAnswer(SetFirewallRulesCommand cmd, String[] results) { - super(cmd, true, null); - + public SetFirewallRulesAnswer(SetFirewallRulesCommand cmd, boolean success, String[] results) { + super(cmd, success, null); assert (cmd.getRules().length == results.length) : "rules and their results should be the same length don't you think?"; this.results = results; } diff --git a/api/src/com/cloud/agent/api/routing/SetFirewallRulesCommand.java b/api/src/com/cloud/agent/api/routing/SetFirewallRulesCommand.java index 3a48e3ffd15..f1c584c978b 100644 --- a/api/src/com/cloud/agent/api/routing/SetFirewallRulesCommand.java +++ b/api/src/com/cloud/agent/api/routing/SetFirewallRulesCommand.java @@ -17,9 +17,13 @@ */ package com.cloud.agent.api.routing; +import java.util.HashSet; import java.util.List; +import java.util.Set; import com.cloud.agent.api.to.FirewallRuleTO; +import com.cloud.agent.api.to.LoadBalancerTO; +import com.cloud.utils.StringUtils; /** * SetFirewallRulesCommand is the transport for firewall rules. @@ -40,4 +44,59 @@ public class SetFirewallRulesCommand extends NetworkElementCommand { public FirewallRuleTO[] getRules() { return rules; } + + public String[][] generateFwRules() { + String [][] result = new String [2][]; + Set toAdd = new HashSet(); + + + for (FirewallRuleTO fwTO: rules) { + /* example : 172.16.92.44:tcp:80:80:0.0.0.0/0:,200.16.92.44:tcp:220:220:0.0.0.0/0:, + * each entry format :protocol:srcport:destport:scidr: + * reverted entry format :reverted:0:0:0: + */ + if (fwTO.revoked() == true) + { + StringBuilder sb = new StringBuilder(); + /* This entry is added just to make sure atleast there will one entry in the list to get the ipaddress */ + sb.append(fwTO.getSrcIp()).append(":reverted:0:0:0:"); + String fwRuleEntry = sb.toString(); + toAdd.add(fwRuleEntry); + continue; + } + + List cidr; + StringBuilder sb = new StringBuilder(); + sb.append(fwTO.getSrcIp()).append(":").append(fwTO.getProtocol()).append(":"); + if ("icmp".compareTo(fwTO.getProtocol()) == 0) + { + sb.append(fwTO.getIcmpType()).append(":").append(fwTO.getIcmpCode()).append(":"); + + }else if (fwTO.getStringSrcPortRange() == null) + sb.append("0:0").append(":"); + else + sb.append(fwTO.getStringSrcPortRange()).append(":"); + + cidr = fwTO.getSourceCidrList(); + if (cidr == null || cidr.isEmpty()) + { + sb.append("0.0.0.0/0"); + }else{ + Boolean firstEntry = true; + for (String tag : cidr) { + if (!firstEntry) sb.append("-"); + sb.append(tag); + firstEntry = false; + } + } + sb.append(":"); + String fwRuleEntry = sb.toString(); + + toAdd.add(fwRuleEntry); + + } + result[0] = toAdd.toArray(new String[toAdd.size()]); + + return result; + } } diff --git a/api/src/com/cloud/agent/api/to/FirewallRuleTO.java b/api/src/com/cloud/agent/api/to/FirewallRuleTO.java index 8a555225dbb..01f2788a284 100644 --- a/api/src/com/cloud/agent/api/to/FirewallRuleTO.java +++ b/api/src/com/cloud/agent/api/to/FirewallRuleTO.java @@ -52,14 +52,21 @@ public class FirewallRuleTO { int[] srcPortRange; boolean revoked; boolean alreadyAdded; + private List sourceCidrList; FirewallRule.Purpose purpose; + private Integer icmpType; + private Integer icmpCode; + protected FirewallRuleTO() { } - public FirewallRuleTO(long id, String srcVlanTag, String srcIp, String protocol, Integer srcPortStart, Integer srcPortEnd, boolean revoked, boolean alreadyAdded, FirewallRule.Purpose purpose) { - this.srcVlanTag = srcVlanTag; - this.srcIp = srcIp; + public FirewallRuleTO(long id, String srcIp, String protocol, Integer srcPortStart, Integer srcPortEnd, boolean revoked, boolean alreadyAdded, FirewallRule.Purpose purpose, List sourceCidr,Integer icmpType,Integer icmpCode) { + this(id,null,srcIp,protocol,srcPortStart,srcPortEnd,revoked,alreadyAdded,purpose,sourceCidr,icmpType,icmpCode); + } + public FirewallRuleTO(long id,String srcVlanTag, String srcIp, String protocol, Integer srcPortStart, Integer srcPortEnd, boolean revoked, boolean alreadyAdded, FirewallRule.Purpose purpose, List sourceCidr,Integer icmpType,Integer icmpCode) { + this.srcVlanTag = srcVlanTag; + this.srcIp = srcIp; this.protocol = protocol; if (srcPortStart != null) { @@ -80,10 +87,16 @@ public class FirewallRuleTO { this.revoked = revoked; this.alreadyAdded = alreadyAdded; this.purpose = purpose; + this.sourceCidrList = sourceCidr; + this.icmpType = icmpType; + this.icmpCode = icmpCode; + } + public FirewallRuleTO(FirewallRule rule, String srcVlanTag, String srcIp) { + this(rule.getId(),srcVlanTag, srcIp, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getState()==State.Revoke, rule.getState()==State.Active, rule.getPurpose(),rule.getSourceCidrList(),rule.getIcmpType(),rule.getIcmpCode()); } - public FirewallRuleTO(FirewallRule rule, String srcVlanTag, String srcIp) { - this(rule.getId(), srcVlanTag, srcIp, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getState()==State.Revoke, rule.getState()==State.Active, rule.getPurpose()); + public FirewallRuleTO(FirewallRule rule, String srcIp) { + this(rule.getId(),null, srcIp, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getState()==State.Revoke, rule.getState()==State.Active, rule.getPurpose(),rule.getSourceCidrList(),rule.getIcmpType(),rule.getIcmpCode()); } public long getId() { @@ -106,14 +119,29 @@ public class FirewallRuleTO { return srcPortRange; } + public Integer getIcmpType(){ + return icmpType; + } + + public Integer getIcmpCode(){ + return icmpCode; + } + public String getStringSrcPortRange() { - return NetUtils.portRangeToString(srcPortRange); + if (srcPortRange == null || srcPortRange.length < 2) + return "0:0"; + else + return NetUtils.portRangeToString(srcPortRange); } public boolean revoked() { return revoked; } + public List getSourceCidrList() { + return sourceCidrList; + } + public boolean isAlreadyAdded() { return alreadyAdded; } diff --git a/api/src/com/cloud/agent/api/to/PortForwardingRuleTO.java b/api/src/com/cloud/agent/api/to/PortForwardingRuleTO.java index 5535177ed6f..64d4542055a 100644 --- a/api/src/com/cloud/agent/api/to/PortForwardingRuleTO.java +++ b/api/src/com/cloud/agent/api/to/PortForwardingRuleTO.java @@ -46,8 +46,8 @@ public class PortForwardingRuleTO extends FirewallRuleTO { this.sourceCidrs = rule.getSourceCidrList(); } - protected PortForwardingRuleTO(long id, String srcVlanTag, String srcIp, int srcPortStart, int srcPortEnd, String dstIp, int dstPortStart, int dstPortEnd, String protocol, boolean revoked, boolean brandNew) { - super(id, srcVlanTag, srcIp, protocol, srcPortStart, srcPortEnd, revoked, brandNew, FirewallRule.Purpose.PortForwarding); + protected PortForwardingRuleTO(long id, String srcIp, int srcPortStart, int srcPortEnd, String dstIp, int dstPortStart, int dstPortEnd, String protocol, boolean revoked, boolean brandNew) { + super(id, srcIp,null, protocol, srcPortStart, srcPortEnd, revoked, brandNew, FirewallRule.Purpose.PortForwarding, null,0,0); this.dstIp = dstIp; this.dstPortRange = new int[] { dstPortStart, dstPortEnd }; } diff --git a/api/src/com/cloud/agent/api/to/StaticNatRuleTO.java b/api/src/com/cloud/agent/api/to/StaticNatRuleTO.java index f9b47522c53..48f46c047ef 100644 --- a/api/src/com/cloud/agent/api/to/StaticNatRuleTO.java +++ b/api/src/com/cloud/agent/api/to/StaticNatRuleTO.java @@ -34,15 +34,20 @@ public class StaticNatRuleTO extends FirewallRuleTO{ protected StaticNatRuleTO() { } - + public StaticNatRuleTO(StaticNatRule rule, String srcVlanTag, String srcIp, String dstIp) { - super(rule.getId(), srcVlanTag, srcIp, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(),rule.getState()==State.Revoke, rule.getState()==State.Active, rule.getPurpose()); + super(rule.getId(),srcVlanTag, srcIp, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(),rule.getState()==State.Revoke, rule.getState()==State.Active, rule.getPurpose(), null,0,0); + this.dstIp = dstIp; + } + + public StaticNatRuleTO(StaticNatRule rule, String scrIp, String dstIp) { + super(rule.getId(), scrIp, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(),rule.getState()==State.Revoke, rule.getState()==State.Active, rule.getPurpose(), null,0,0); this.dstIp = dstIp; } - protected StaticNatRuleTO(long id, String srcVlanTag, String srcIp, int srcPortStart, int srcPortEnd, String dstIp, int dstPortStart, int dstPortEnd, String protocol, boolean revoked, boolean brandNew) { - super(id, srcVlanTag, srcIp, protocol, srcPortStart, srcPortEnd, revoked, brandNew, FirewallRule.Purpose.StaticNat); + public StaticNatRuleTO(long id, String srcIp, Integer srcPortStart, Integer srcPortEnd, String dstIp, Integer dstPortStart, Integer dstPortEnd, String protocol, boolean revoked, boolean alreadyAdded) { + super(id, srcIp, protocol, srcPortStart, srcPortEnd, revoked, alreadyAdded, FirewallRule.Purpose.StaticNat, null,0,0); this.dstIp = dstIp; } diff --git a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 2d8b560bcac..74b494ce947 100644 --- a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -156,7 +156,6 @@ import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; import com.cloud.agent.api.storage.DestroyCommand; import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; -import com.cloud.agent.api.to.FirewallRuleTO; import com.cloud.agent.api.to.IpAddressTO; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.PortForwardingRuleTO; @@ -6508,17 +6507,37 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return new Answer(cmd, success, ""); } - protected SetFirewallRulesAnswer execute(SetFirewallRulesCommand cmd) { - Connection conn = getConnection(); - - String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); - String[] results = new String[cmd.getRules().length]; - int i = 0; - for (FirewallRuleTO rule : cmd.getRules()) { - //FIXME - Jana, add implementation here - } + protected SetFirewallRulesAnswer execute(SetFirewallRulesCommand cmd) { + String[] results = new String[cmd.getRules().length]; + String callResult; + Connection conn = getConnection(); + String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); - return new SetFirewallRulesAnswer(cmd, results); - } - + if (routerIp == null) { + return new SetFirewallRulesAnswer(cmd, false, results); + } + + String[][] rules = cmd.generateFwRules(); + String args = ""; + args += routerIp + " -F "; + StringBuilder sb = new StringBuilder(); + String[] fwRules = rules[0]; + if (fwRules.length > 0) { + for (int i = 0; i < fwRules.length; i++) { + sb.append(fwRules[i]).append(','); + } + args += " -a " + sb.toString(); + } + + callResult = callHostPlugin(conn, "vmops", "setFirewallRule", "args", args); + + if (callResult == null || callResult.isEmpty()) { + //FIXME - in the future we have to process each rule separately; now we temporarily set every rule to be false if single rule fails + for (int i=0; i < results.length; i++) { + results[i] = "Failed"; + } + return new SetFirewallRulesAnswer(cmd, false, results); + } + return new SetFirewallRulesAnswer(cmd, true, results); + } } diff --git a/patches/systemvm/debian/config/root/firewall_rule.sh b/patches/systemvm/debian/config/root/firewall_rule.sh new file mode 100755 index 00000000000..41d3927315a --- /dev/null +++ b/patches/systemvm/debian/config/root/firewall_rule.sh @@ -0,0 +1,188 @@ +#!/usr/bin/env bash +# +# 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 . +# +# firewall_rule.sh -- allow some ports / protocols to vm instances +# +# +# @VERSION@ + +usage() { + printf "Usage: %s: -a \n" $(basename $0) >&2 + printf "sourcecidrs format: cidr1-cidr2-cidr3-...\n" +} +#set -x +#FIXME: eating up the error code during execution of iptables +fw_remove_backup() { + local pubIp=$1 + sudo iptables -t mangle -F _FIREWALL_$pubIp 2> /dev/null + sudo iptables -t mangle -D PREROUTING -d $pubIp -j _FIREWALL_$pubIp 2> /dev/null + sudo iptables -t mangle -X _FIREWALL_$pubIp 2> /dev/null +} + +fw_restore() { + local pubIp=$1 + sudo iptables -t mangle -F FIREWALL_$pubIp 2> /dev/null + sudo iptables -t mangle -D PREROUTING -d $pubIp -j FIREWALL_$pubIp 2> /dev/null + sudo iptables -t mangle -X FIREWALL_$pubIp 2> /dev/null + sudo iptables -t mangle -E _FIREWALL_$pubIp FIREWALL_$pubIp 2> /dev/null +} + +fw_chain_for_ip () { + local pubIp=$1 + fw_remove_backup $1 + sudo iptables -t mangle -E FIREWALL_$pubIp _FIREWALL_$pubIp 2> /dev/null + sudo iptables -t mangle -N FIREWALL_$pubIp 2> /dev/null + # drop if no rules match (this will be the last rule in the chain) + sudo iptables -t mangle -A FIREWALL_$pubIp -j DROP> /dev/null + # ensure outgoing connections are maintained (first rule in chain) + sudo iptables -t mangle -I FIREWALL_$pubIp -m state --state RELATED,ESTABLISHED -j ACCEPT> /dev/null + #ensure that this table is after VPN chain + sudo iptables -t mangle -I PREROUTING 2 -d $pubIp -j FIREWALL_$pubIp +} + +fw_entry_for_public_ip() { + local rules=$1 + + local pubIp=$(echo $rules | cut -d: -f1) + local prot=$(echo $rules | cut -d: -f2) + local sport=$(echo $rules | cut -d: -f3) + local eport=$(echo $rules | cut -d: -f4) + local scidrs=$(echo $rules | cut -d: -f5 | sed 's/-/ /g') + + logger -t cloud "$(basename $0): enter apply firewall rules for public ip $pubIp:$prot:$sport:$eport:$scidrs" + + + # note that rules are inserted after the RELATED,ESTABLISHED rule + # but before the DROP rule + for src in $scidrs + do + [ "$prot" == "reverted" ] && continue; + if [ "$prot" == "icmp" ] + then + typecode="$sport/$eport" + [ "$eport" == "-1" ] && typecode="$sport" + [ "$sport" == "-1" ] && typecode="any" + sudo iptables -t mangle -I FIREWALL_$pubIp 2 -s $src -p $prot \ + --icmp-type $typecode -j RETURN + else + sudo iptables -t mangle -I FIREWALL_$pubIp 2 -s $src -p $prot \ + --dport $sport:$eport -j RETURN + fi + result=$? + [ $result -gt 0 ] && + logger -t cloud "Error adding iptables entry for $pubIp:$prot:$sport:$eport:$src" && + break + done + + logger -t cloud "$(basename $0): exit apply firewall rules for public ip $pubIp" + return $result +} + +get_vif_list() { + local vif_list="" + for i in /sys/class/net/eth*; do + vif=$(basename $i); + if [ "$vif" != "eth0" ] && [ "$vif" != "eth1" ] + then + vif_list="$vif_list $vif"; + fi + done + if [ "$vif_list" == "" ] + then + vif_list="eth0" + fi + + logger -t cloud "FirewallRule public interfaces = $vif_list" + echo $vif_list +} + +shift +rules= +while getopts 'a:' OPTION +do + case $OPTION in + a) aflag=1 + rules="$OPTARG" + ;; + ?) usage + exit 2 + ;; + esac +done + +VIF_LIST=$(get_vif_list) + +if [ "$rules" == "" ] +then + rules="none" +fi + +#-a 172.16.92.44:tcp:80:80:0.0.0.0/0:,172.16.92.44:tcp:220:220:0.0.0.0/0:,172.16.92.44:tcp:222:222:192.168.10.0/24-75.57.23.0/22-88.100.33.1/32 +# if any entry is reverted , entry will be in the format :reverted:0:0:0 +# example : 172.16.92.44:tcp:80:80:0.0.0.0/0:,172.16.92.44:tcp:220:220:0.0.0.0/0:,200.1.1.2:reverted:0:0:0 +# The reverted entries will fix the following partially +#FIXME: rule leak: when there are multiple ip address, there will chance that entry will be left over if the ipadress does not appear in the current execution when compare to old one +# example : In the below first transaction have 2 ip's whereas in second transaction it having one ip, so after the second trasaction 200.1.2.3 ip will have rules in mangle table. +# 1) -a 172.16.92.44:tcp:80:80:0.0.0.0/0:,200.16.92.44:tcp:220:220:0.0.0.0/0:, +# 2) -a 172.16.92.44:tcp:80:80:0.0.0.0/0:,172.16.92.44:tcp:220:220:0.0.0.0/0:, + + +success=0 +publicIps= +rules_list=$(echo $rules | cut -d, -f1- --output-delimiter=" ") +for r in $rules_list +do + pubIp=$(echo $r | cut -d: -f1) + publicIps="$pubIp $publicIps" +done + +unique_ips=$(echo $publicIps| tr " " "\n" | sort | uniq | tr "\n" " ") + +for u in $unique_ips +do + fw_chain_for_ip $u +done + +for r in $rules_list +do + pubIp=$(echo $r | cut -d: -f1) + fw_entry_for_public_ip $r + success=$? + if [ $success -gt 0 ] + then + logger -t cloud "$(basename $0): failure to apply fw rules for ip $pubIp" + break + else + logger -t cloud "$(basename $0): successful in applying fw rules for ip $pubIp" + fi +done + +if [ $success -gt 0 ] +then + for p in $unique_ips + do + logger -t cloud "$(basename $0): restoring from backup for ip: $p" + fw_restore $p + done +fi +for p in $unique_ips +do + logger -t cloud "$(basename $0): deleting backup for ip: $p" + fw_remove_backup $p +done +exit $success + diff --git a/scripts/network/domr/call_firewall.sh b/scripts/network/domr/call_firewall.sh index 29765e657d9..1e4dc939eaf 100755 --- a/scripts/network/domr/call_firewall.sh +++ b/scripts/network/domr/call_firewall.sh @@ -24,12 +24,12 @@ # firewall.sh -- allow some ports / protocols to vm instances # # - usage() { - printf "Usage: %s: (-A|-D) -i -r -P protocol (-p port_range | -t icmp_type_code) -l -d [-f -u -y -z ] \n" $(basename $0) >&2 + printf "Usage for Firewall rule : %s: -F " $(basename $0) >&2 + printf "Usage for other purposes : %s: (-A|-D) -i -r -P protocol (-p port_range | -t icmp_type_code) -l -d [-f -u -y -z ] \n" $(basename $0) >&2 } -# set -x +#set -x # check if gateway domain is up and running check_gw() { @@ -52,9 +52,22 @@ if [ $? -gt 0 ] then exit 1 fi +fflag= +while getopts ':F' OPTION +do + case $OPTION in + F) fflag=1 + ;; + \?) ;; + esac +done - -ssh -p 3922 -q -o StrictHostKeyChecking=no -i $cert root@$domRIp "/root/firewall.sh $*" +if [ -n "$fflag" ] +then + ssh -p 3922 -q -o StrictHostKeyChecking=no -i $cert root@$domRIp "/root/firewall_rule.sh $*" +else + ssh -p 3922 -q -o StrictHostKeyChecking=no -i $cert root@$domRIp "/root/firewall.sh $*" +fi exit $?