add security group support for kvm

This commit is contained in:
edison 2010-12-16 17:59:51 -08:00
parent f7554a94e1
commit 19431d31d3
2 changed files with 748 additions and 158 deletions

View File

@ -113,6 +113,7 @@ import com.cloud.agent.api.SecurityIngressRuleAnswer;
import com.cloud.agent.api.SecurityIngressRulesCmd;
import com.cloud.agent.api.PingCommand;
import com.cloud.agent.api.PingRoutingCommand;
import com.cloud.agent.api.PingRoutingWithNwGroupsCommand;
import com.cloud.agent.api.PingTestCommand;
import com.cloud.agent.api.PrepareForMigrationAnswer;
import com.cloud.agent.api.PrepareForMigrationCommand;
@ -227,6 +228,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
private String _manageSnapshotPath;
private String _createTmplPath;
private String _heartBeatPath;
private String _securityGroupPath;
private String _host;
private String _dcId;
private String _pod;
@ -616,6 +618,11 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
throw new ConfigurationException("Unable to find the createtmplt.sh");
}
_securityGroupPath = Script.findScript(networkScriptsDir, "security_group.py");
if (_securityGroupPath == null) {
throw new ConfigurationException("Unable to find the security_group.py");
}
String value = (String)params.get("developer");
boolean isDeveloper = Boolean.parseBoolean(value);
@ -754,8 +761,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
}
}
//_can_bridge_firewall = can_bridge_firewall();
Network vmopsNw = null;
try {
vmopsNw = _conn.networkLookupByName(_privNwName);
@ -802,6 +807,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
}
s_logger.debug("Found pif: " + _pifs.first() + " on " + _privBridgeName + ", pif: " + _pifs.second() + " on " + _publicBridgeName);
_can_bridge_firewall = can_bridge_firewall(_pifs.second());
_localGateway = Script.runSimpleBashScript("ip route |grep default|awk '{print $3}'");
if (_localGateway == null) {
s_logger.debug("Failed to found the local gateway");
@ -1776,8 +1784,22 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
}
return answer;
}
private Answer execute(SecurityIngressRulesCmd cmd) {
return new SecurityIngressRuleAnswer(cmd);
boolean result = add_network_rules(cmd.getVmName(),
Long.toString(cmd.getVmId()),
cmd.getGuestIp(),cmd.getSignature(),
Long.toString(cmd.getSeqNum()),
cmd.getGuestMac(),
cmd.stringifyRules());
if (!result) {
s_logger.warn("Failed to program network rules for vm " + cmd.getVmName());
return SecurityIngressRuleAnswer(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 SecurityIngressRuleAnswer(cmd);
}
}
protected GetVncPortAnswer execute(GetVncPortCommand cmd) {
@ -2194,8 +2216,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
_vms.put(vmName, State.Stopping);
}
try {
/*if (isDirectAttachedNetwork(cmd.getVnet()))
destroy_network_rules_for_vm(vmName);*/
destroy_network_rules_for_vm(vmName);
String result = stopVM(vmName, defineOps.UNDEFINE_VM);
answer = new StopAnswer(cmd, null, 0, bytesSent, bytesReceived);
@ -2411,24 +2432,11 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
s_logger.debug("starting " + vmName + ": " + vm.toString());
startDomain(vmName, vm.toString());
Monitor monitor = vmSpec.getMonitor();
if (monitor != null && monitor instanceof SshMonitor) {
SshMonitor sshMon = (SshMonitor)monitor;
String privateIp = sshMon.getIp();
int cmdPort = sshMon.getPort();
if (s_logger.isDebugEnabled()) {
s_logger.debug("Ping command port, " + privateIp + ":" + cmdPort);
}
String result = _virtRouterResource.connect(privateIp, cmdPort);
if (result != null) {
throw new CloudRuntimeException("Can not ping System vm " + vmName + "due to:" + result);
}
if (s_logger.isDebugEnabled()) {
s_logger.debug("Ping command port succeeded for vm " + vmName);
}
if (vmSpec.getType() != VirtualMachine.Type.User) {
default_network_rules_for_systemvm(vmName);
} else {
default_network_rules(vmName, vmSpec.getNics()[0].getIp(), vmSpec.getId(), vmSpec.getNics()[0].getMac());
}
// Attach each data volume to the VM, if there is a deferred attached disk
@ -2701,8 +2709,13 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
@Override
public PingCommand getCurrentStatus(long id) {
final HashMap<String, State> newStates = sync();
return new PingRoutingCommand(com.cloud.host.Host.Type.Routing, id, newStates);
final HashMap<String, State> newStates = sync();
if (!_can_bridge_firewall) {
return new PingRoutingCommand(com.cloud.host.Host.Type.Routing, id, newStates);
} else {
HashMap<String, Pair<Long, Long>> nwGrpStates = syncNetworkGroups(id);
return new PingRoutingWithNwGroupsCommand(getType(), id, newStates, nwGrpStates);
}
}
@Override
@ -3464,57 +3477,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return disks;
}
private boolean can_bridge_firewall() {
String command = "iptables -N BRIDGE-FIREWALL; " +
"iptables -I BRIDGE-FIREWALL -m state --state RELATED,ESTABLISHED -j ACCEPT";
String result = executeBashScript(command);
if (result != null) {
s_logger.debug("Chain BRIDGE-FIREWALL already exists");
}
boolean enabled = true;
result = executeBashScript("iptables -n -L FORWARD|grep BRIDGE-FIREWALL");;
if (result != null) {
result = executeBashScript("iptables -I FORWARD -m physdev --physdev-is-bridged -j BRIDGE-FIREWALL");;
if (result != null) {
enabled = false;
}
}
File logPath = new File("/var/run/cloud");
if (!logPath.exists()) {
logPath.mkdirs();
}
cleanup_rules_for_dead_vms();
cleanup_rules();
return enabled;
}
private void cleanup_rules_for_dead_vms() {
}
private void cleanup_rules() {
}
/* private void ipset(String ipsetname, String proto, String start, String end, List<String> ips) {
Script command = new Script("/bin/bash", _timeout, s_logger);
command.add("-c");
command.add("ipset -N " + ipsetname + " iptreemap");
String result = command.execute();
if (result != null) {
s_logger.debug("ipset chain already exists: " + ipsetname);
}
boolean success = true;
String ipsettmp = ipsetname +
}
*/
private Domain getDomain(String vmName) throws LibvirtException {
return _conn.domainLookupByUUID(UUID.nameUUIDFromBytes(vmName.getBytes()));
}
@ -3559,88 +3521,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return command.execute(parser);
}
private boolean default_network_rules_for_systemvm(String vmName) {
String command;
List<String> nics = getInterfaces(vmName);
for (String vif : nics) {
command = "iptables -D BRIDGE-FIREWALL -m physdev --physdev-is-bridged --physdev-out " + vif + " -j " + vmName + ";" +
"iptables -D BRIDGE-FIREWALL -m physdev --physdev-is-bridged --physdev-in " + vif + " -j " + vmName;
String result = executeBashScript(command);
if (result != null) {
s_logger.debug("Ingnoring failure to delete old rules");
}
command = "iptables -N " + vmName;
result = executeBashScript(command);
if (result != null) {
command = "iptables -F " + vmName;
result = executeBashScript(command);
}
command = "iptables -A BRIDGE-FIREWALL -m physdev --physdev-is-bridged --physdev-out " + vif + " -j " + vmName + ";" +
"iptables -A BRIDGE-FIREWALL -m physdev --physdev-is-bridged --physdev-in " + vif + " -j " + vmName;
result = executeBashScript(command);
if (result != null) {
s_logger.debug("Failed to program default rules");
return false;
}
}
command = "iptables -A " + vmName + " -j ACCEPT";
executeBashScript(command);
return true;
}
private boolean default_network_rules(String vmName, String vmIP) {
String command;
List<String> nics = getInterfaces(vmName);
for (String vif : nics) {
command = "iptables -D BRIDGE-FIREWALL -m physdev --physdev-is-bridged --physdev-out " + vif + " -j " + vmName + ";" +
"iptables -D BRIDGE-FIREWALL -m physdev --physdev-is-bridged --physdev-in " + vif + " -j " + vmName + ";" +
"iptables -F " + vmName + ";" +
"iptables -X " + vmName + ";";
String result = executeBashScript(command);
if (result != null) {
s_logger.debug("Ignoring failure to delete old rules");
}
result = executeBashScript("iptables -N " + vmName);
if (result != null) {
executeBashScript("iptables -F " + vmName);
}
command = "iptables -D BRIDGE-FIREWALL -m physdev --physdev-is-bridged --physdev-out " + vif + " -j " + vmName + ";" +
"iptables -D BRIDGE-FIREWALL -m physdev --physdev-is-bridged --physdev-in " + vif + " -j " + vmName + ";" +
"iptables -A " + vmName + " -m state --state RELATED,ESTABLISHED -j ACCEPT" + ";" +
"iptables -A " + vmName + " -p udp --dport 67:68 --sport 67:68 -j ACCEPT" + ";" +
"iptables -A " + vmName + " -m physdev --physdev-is-bridged --physdev-in " + vif + " --source " + vmIP + " -j RETURN" + ";" +
"iptables -A " + vmName + " -j DROP";
result = executeBashScript(command);
if (result != null) {
s_logger.debug("Failed to program default rules for vm:" + vmName);
return false;
}
}
return true;
}
private boolean destroy_network_rules_for_vm(String vmName) {
String command = "iptables-save |grep BRIDGE-FIREWALL |grep " + vmName + " | sed 's/-A/-D/'";
OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
String result = executeBashScript(command, parser);
if (result == null && parser.getLines() != null) {
String[] lines = parser.getLines().split("\\n");
for (String cmd : lines) {
command = "iptables " + cmd;
executeBashScript(command);
}
}
executeBashScript("iptables -F " + vmName);
executeBashScript("iptables -X " + vmName);
return true;
}
private void deletExitingLinkLocalRoutTable(String linkLocalBr) {
Script command = new Script("/bin/bash", _timeout);
command.add("-c");
@ -3852,4 +3732,112 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
}
}
}
private boolean can_bridge_firewall(String prvNic) {
Script cmd = new Script(_securityGroupPath, _timeout, s_logger);
cmd.add("can_bridge_firewall");
cmd.add(prvNic);
String result = cmd.execute();
if (result != null) {
return false;
}
return true;
}
private boolean destroy_network_rules_for_vm(String vmName) {
if (!_can_bridge_firewall)
return false;
Script cmd = new Script(_securityGroupPath, _timeout, s_logger);
cmd.add("destroy_network_rules_for_vm");
cmd.add("--vmname");
cmd.add(vmName);
String result = cmd.execute();
if (result != null) {
return false;
}
return true;
}
private boolean default_network_rules(String vmName, String pubIP, long vmId, String mac) {
if (!_can_bridge_firewall)
return false;
Script cmd = new Script(_securityGroupPath, _timeout, s_logger);
cmd.add("default_network_rules");
cmd.add("--vmname", vmName);
cmd.add("--vmip", pubIP);
cmd.add("--vmid", Long.toString(vmId));
cmd.add("--vmmac", mac);
String result = cmd.execute();
if (result != null) {
return false;
}
return true;
}
private boolean default_network_rules_for_systemvm(String vmName) {
if (!_can_bridge_firewall)
return false;
Script cmd = new Script(_securityGroupPath, _timeout, s_logger);
cmd.add("default_network_rules_systemvm");
cmd.add("--vmname");
cmd.add(vmName);
String result = cmd.execute();
if (result != null) {
return false;
}
return true;
}
private boolean add_network_rules(String vmName, String vmId, String guestIP, String sig, String seq, String mac, String rules) {
if (!_can_bridge_firewall)
return false;
String newRules = rules.replace(" ", ";");
Script cmd = new Script(_securityGroupPath, _timeout, s_logger);
cmd.add("add_network_rules");
cmd.add("--vmname", vmName);
cmd.add("--vmid", vmId);
cmd.add("--vmip", guestIP);
cmd.add("--sig", sig);
cmd.add("--seq", seq);
cmd.add("--vmmac", mac);
cmd.add("--rules", newRules);
String result = cmd.execute();
if (result != null) {
return false;
}
return true;
}
private String get_rule_logs_for_vms() {
Script cmd = new Script(_securityGroupPath, _timeout, s_logger);
cmd.add("get_rule_logs_for_vms");
OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser();
String result = cmd.execute(parser);
if (result == null) {
return parser.getLine();
}
return null;
}
private HashMap<String, Pair<Long,Long>> syncNetworkGroups(long id) {
HashMap<String, Pair<Long,Long>> states = new HashMap<String, Pair<Long,Long>>();
String result = get_rule_logs_for_vms();
s_logger.trace("syncNetworkGroups: id=" + id + " got: " + result);
String [] rulelogs = result != null ?result.split(";"): new String [0];
for (String rulesforvm: rulelogs){
String [] log = rulesforvm.split(",");
if (log.length != 6) {
continue;
}
try {
states.put(log[0], new Pair<Long,Long>(Long.parseLong(log[1]), Long.parseLong(log[5])));
} catch (NumberFormatException nfe) {
states.put(log[0], new Pair<Long,Long>(-1L, -1L));
}
}
return states;
}
}

View File

@ -0,0 +1,602 @@
#!/usr/bin/python
import cloud_utils
from cloud_utils import Command
import logging
import sys
import os
import xml.dom.minidom
from optparse import OptionParser, OptionGroup, OptParseError, BadOptionError, OptionError, OptionConflictError, OptionValueError
iptables = Command("iptables")
bash = Command("/bin/bash")
virsh = Command("virsh")
ebtablessave = Command("ebtables-save")
ebtables = Command("ebtables")
augtool = Command("augtool")
def execute(cmd):
logging.debug(cmd)
return bash("-c", cmd).stdout
def can_bridge_firewall(privnic):
try:
execute("which iptables")
except:
print "no iptables on your host machine"
exit(1)
try:
execute("which ebtables")
except:
print "no ebtables on your host machine"
exit(2)
try:
alreadySetup = augtool.match("/files/etc/sysctl.conf/net.bridge.bridge-nf-call-arptables", "1").stdout.strip()
if len(alreadySetup) == 0:
script = """
set /files/etc/sysctl.conf/net.bridge.bridge-nf-call-arptables 1
save"""
augtool < script
alreadySetup = augtool.match("/files/etc/sysctl.conf/net.bridge.bridge-nf-call-iptables", "1").stdout.strip()
if len(alreadySetup) == 0:
script = """
set /files/etc/sysctl.conf/net.bridge.bridge-nf-call-iptables 1
save"""
augtool < script
alreadySetup = augtool.match("/files/etc/sysctl.conf/net.bridge.bridge-nf-call-ip6tables", "1").stdout.strip()
if len(alreadySetup) == 0:
script = """
set /files/etc/sysctl.conf/net.bridge.bridge-nf-call-ip6tables 1
save"""
augtool < script
execute("sysctl -p /etc/sysctl.conf")
except:
print "failed to turn on bridge netfilter"
exit(3)
try:
execute("iptables -N BRIDGE-FIREWALL")
execute("iptables -N BRIDGE-FIREWALL")
execute("iptables -I BRIDGE-FIREWALL -m state --state RELATED,ESTABLISHED -j ACCEPT")
execute("iptables -D FORWARD -j RH-Firewall-1-INPUT")
except:
logging.exception('Chain BRIDGE-FIREWALL already exists: ')
result = 0
try:
execute("iptables -n -L FORWARD | grep BRIDGE-FIREWALL")
except:
try:
execute("iptables -I FORWARD -m physdev --physdev-is-bridged -j BRIDGE-FIREWALL")
execute("iptables -A FORWARD -m physdev --physdev-is-bridged --physdev-out " + privnic + " -j ACCEPT")
execute("iptables -A FORWARD -j DROP")
except:
result = 1
if result == 1:
print "br firewall is not supported"
if not os.path.exists('/var/run/cloud'):
os.makedirs('/var/run/cloud')
cleanup_rules_for_dead_vms()
cleanup_rules()
return result
'''
def ipset(ipsetname, proto, start, end, ips):
try:
check_call(['ipset', '-N', ipsetname, 'iptreemap'])
except:
logging.debug("ipset chain already exists" + ipsetname)
result = True
ipsettmp = ''.join(''.join(ipsetname.split('-')).split('_')) + str(int(time.time()) % 1000)
try:
check_call(['ipset', '-N', ipsettmp, 'iptreemap'])
for ip in ips:
try:
check_call(['ipset', '-A', ipsettmp, ip])
except CommandException, cex:
if cex.reason.rfind('already in set') == -1:
raise
check_call(['ipset', '-W', ipsettmp, ipsetname])
check_call(['ipset', '-X', ipsettmp])
except:
logging.debug("Failed to program ipset " + ipsetname)
result = False
return result
'''
def destroy_network_rules_for_vm(vm_name):
vmchain = vm_name
delete_rules_for_vm_in_bridge_firewall_chain(vm_name)
if vm_name.startswith('i-') or vm_name.startswith('r-'):
vmchain = '-'.join(vm_name.split('-')[:-1])
vmchain_default = '-'.join(vm_name.split('-')[:-2]) + "-def"
destroy_ebtables_rules(vmchain)
try:
execute("iptables -F " + vmchain_default)
execute("iptables -X " + vmchain_default)
except:
logging.exception("Ignoring failure to delete chain " + vmchain_default)
try:
execute("iptables -F " + vmchain)
execute("iptables -X " + vmchain)
except:
logging.debug("Ignoring failure to delete chain " + vmchain)
remove_rule_log_for_vm(vm_name)
if 1 in [ vm_name.startswith(c) for c in ['r-', 's-', 'v-'] ]:
return 'true'
return 'true'
def destroy_ebtables_rules(vm_name):
delcmd = "ebtables-save | grep ROUTING | grep " + vm_name + " | sed 's/-A/-D/'"
delcmds = execute(delcmd).split('\n')
delcmds.pop()
for cmd in delcmds:
try:
execute("ebtables -t nat " + cmd)
except:
logging.debug("Ignoring failure to delete ebtables rules for vm " + vm_name)
chains = [vm_name+"-in", vm_name+"-out"]
for chain in chains:
try:
execute("ebtables -t nat -F " + chain)
execute("ebtables -t nat -X " + chain)
except:
logging.debug("Ignoring failure to delete ebtables chain for vm " + vm_name)
def default_ebtables_rules(vm_name, vif, vm_ip, vm_mac):
vmchain_in = vm_name + "-in"
vmchain_out = vm_name + "-out"
for chain in [vmchain_in, vmchain_out]:
try:
execute("ebtables -t nat -N " + chain)
except:
execute("ebtables -t nat -F " + chain)
try:
# -s ! 52:54:0:56:44:32 -j DROP
execute("ebtables -t nat -A PREROUTING -i " + vif + " -j " + vmchain_in)
execute("ebtables -t nat -A POSTROUTING -o " + vif + " -j " + vmchain_out)
except:
logging.debug("Failed to program default rules")
return 'false'
try:
execute("ebtables -t nat -A " + vmchain_in + " -i " + vif + " -s ! " + vm_mac + " -j DROP")
execute("ebtables -t nat -A " + vmchain_in + " -p ARP -s ! " + vm_mac + " -j DROP")
execute("ebtables -t nat -A " + vmchain_in + " -p ARP --arp-mac-src ! " + vm_mac + " -j DROP")
execute("ebtables -t nat -A " + vmchain_in + " -p ARP --arp-ip-src ! " + vm_ip + " -j DROP")
execute("ebtables -t nat -A " + vmchain_in + " -p ARP --arp-op Request -j ACCEPT")
execute("ebtables -t nat -A " + vmchain_in + " -p ARP --arp-op Reply -j ACCEPT")
execute("ebtables -t nat -A " + vmchain_in + " -p ARP -j DROP")
except:
logging.exception("Failed to program default ebtables IN rules")
return 'false'
try:
execute("ebtables -t nat -A " + vmchain_out + " -p ARP --arp-op Reply --arp-mac-dst ! " + vm_mac + " -j DROP")
execute("ebtables -t nat -A " + vmchain_out + " -p ARP --arp-ip-dst ! " + vm_ip + " -j DROP")
execute("ebtables -t nat -A " + vmchain_out + " -p ARP --arp-op Request -j ACCEPT")
execute("ebtables -t nat -A " + vmchain_out + " -p ARP --arp-op Reply -j ACCEPT")
execute("ebtables -t nat -A " + vmchain_out + " -p ARP -j DROP")
except:
logging.debug("Failed to program default ebtables OUT rules")
return 'false'
def default_network_rules_systemvm(vm_name):
vifs = getVifs(vm_name)
domid = getvmId(vm_name)
vmchain = vm_name
if vm_name.startswith('r-'):
vmchain = '-'.join(vm_name.split('-')[:-1])
delete_rules_for_vm_in_bridge_firewall_chain(vm_name)
try:
execute("iptables -N " + vmchain)
except:
execute("iptables -F " + vmchain)
for vif in vifs:
try:
execute("iptables -A BRIDGE-FIREWALL -m physdev --physdev-is-bridged --physdev-out " + vif + " -j " + vmchain)
execute("iptables -A BRIDGE-FIREWALL -m physdev --physdev-is-bridged --physdev-in " + vif + " -j " + vmchain)
except:
logging.debug("Failed to program default rules")
return 'false'
execute("iptables -A " + vmchain + " -j ACCEPT")
if write_rule_log_for_vm(vm_name, '-1', '_ignore_', domid, '_initial_', '-1') == False:
logging.debug("Failed to log default network rules for systemvm, ignoring")
return 'true'
def default_network_rules(vm_name, vm_ip, vm_id, vm_mac):
print vm_name
print vm_ip
print vm_mac
vmName = vm_name
domID = getvmId(vm_name)
delete_rules_for_vm_in_bridge_firewall_chain(vmName)
vm_name = '-'.join(vm_name.split('-')[:-1])
vmchain = vm_name
vmchain_default = '-'.join(vmchain.split('-')[:-1]) + "-def"
destroy_ebtables_rules(vmName)
vifs = getVifs(vmName)
try:
execute("iptables -N " + vmchain)
except:
execute("iptables -F " + vmchain)
try:
execute("iptables -N " + vmchain_default)
except:
execute("iptables -F " + vmchain_default)
try:
for v in vifs:
execute("iptables -A BRIDGE-FIREWALL -m physdev --physdev-is-bridged --physdev-out " + v + " -j " + vmchain_default)
execute("iptables -A BRIDGE-FIREWALL -m physdev --physdev-is-bridged --physdev-in " + v + " -j " + vmchain_default)
execute("iptables -A " + vmchain_default + " -m state --state RELATED,ESTABLISHED -j ACCEPT")
#allow dhcp
for v in vifs:
execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + v + " -p udp --dport 67 --sport 68 -j ACCEPT")
execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-out " + v + " -p udp --dport 68 --sport 67 -j ACCEPT")
#don't let vm spoof its ip address
for v in vifs:
execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + v + " --source " + vm_ip + " -j RETURN")
execute("iptables -A " + vmchain_default + " -j " + vmchain)
except:
logging.debug("Failed to program default rules for vm " + vm_name)
return 'false'
for v in vifs:
default_ebtables_rules(vmchain, v, vm_ip, vm_mac)
if write_rule_log_for_vm(vmName, vm_id, vm_ip, domID, '_initial_', '-1') == False:
logging.debug("Failed to log default network rules, ignoring")
logging.debug("Programmed default rules for vm " + vm_name)
return 'true'
def delete_rules_for_vm_in_bridge_firewall_chain(vmName):
vm_name = vmName
if vm_name.startswith('i-') or vm_name.startswith('r-'):
vm_name = '-'.join(vm_name.split('-')[:-2])
vmchain = vm_name
delcmd = "iptables -S BRIDGE-FIREWALL | grep " + vmchain + " | sed 's/-A/-D/'"
delcmds = execute(delcmd).split('\n')
delcmds.pop()
for cmd in delcmds:
try:
execute("iptables " + cmd)
except:
logging.exception("Ignoring failure to delete rules for vm " + vmName)
'''
def network_rules_for_rebooted_vm(vmName):
vm_name = vmName
vifs = getVifs(vmName)
logging.debug("Found a rebooted VM -- reprogramming rules for " + vmName)
delete_rules_for_vm_in_bridge_firewall_chain(vmName)
if 1 in [ vm_name.startswith(c) for c in ['r-', 's-', 'v-'] ]:
default_network_rules_systemvm(session, {"vmName":vmName})
return True
vmchain = '-'.join(vm_name.split('-')[:-1])
vmchain_default = '-'.join(vm_name.split('-')[:-2]) + "-def"
for v in vifs:
iptables('-A', 'BRIDGE-FIREWALL', '-m', 'physdev', '--physdev-is-bridged', '--physdev-out', v, '-j', vmchain_default)
iptables('-A', 'BRIDGE-FIREWALL', '-m', 'physdev', '--physdev-is-bridged', '--physdev-in', v, '-j', vmchain_default)
#change antispoof rule in vmchain
try:
delcmd = "iptables -S " + vmchain_default + " | grep physdev-in | sed 's/-A/-D/'"
inscmd = "iptables -S " + vmchain_default + " | grep physdev-in | grep vif | sed -r 's/vif[0-9]+.0/" + vif + "/' | sed 's/-A/-I/'"
inscmd2 = "iptables -S " + vmchain_default + " | grep physdev-in | grep tap | sed -r 's/tap[0-9]+.0/" + tap + "/' | sed 's/-A/-I/'"
ipts = []
for cmd in [delcmd, inscmd, inscmd2]:
cmds = bash('-c', cmd.split(' ')).split('\n')
cmds.pop()
for c in cmds:
ipt = c.split(' ')
ipt.pop()
ipts.append(ipt)
for ipt in ipts:
try:
iptables(ipt)
except:
logging.debug("Failed to rewrite antispoofing rules for vm " + vmName)
except:
logging.debug("No rules found for vm " + vmchain)
rewrite_rule_log_for_vm(vmName, curr_domid)
return True
'''
def rewrite_rule_log_for_vm(vm_name, new_domid):
logfilename = "/var/run/cloud/" + vm_name +".log"
if not os.path.exists(logfilename):
return
lines = (line.rstrip() for line in open(logfilename))
[_vmName,_vmID,_vmIP,_domID,_signature,_seqno] = ['_', '-1', '_', '-1', '_', '-1']
for line in lines:
[_vmName,_vmID,_vmIP,_domID,_signature,_seqno] = line.split(',')
break
write_rule_log_for_vm(_vmName, _vmID, '0.0.0.0', new_domid, _signature, '-1')
def get_rule_log_for_vm(vmName):
vm_name = vmName;
logfilename = "/var/run/cloud/" + vm_name +".log"
if not os.path.exists(logfilename):
return ''
lines = (line.rstrip() for line in open(logfilename))
[_vmName,_vmID,_vmIP,_domID,_signature,_seqno] = ['_', '-1', '_', '-1', '_', '-1']
for line in lines:
[_vmName,_vmID,_vmIP,_domID,_signature,_seqno] = line.split(',')
break
return ','.join([_vmName, _vmID, _vmIP, _domID, _signature, _seqno])
def get_rule_logs_for_vms():
cmd = "virsh list|grep running |awk '{print $2}'"
vms = bash("-c", cmd).stdout.split("\n")
result = []
try:
for name in vms:
name = name.rstrip()
if 1 not in [ name.startswith(c) for c in ['r-', 's-', 'v-', 'i-'] ]:
continue
#network_rules_for_rebooted_vm(session, name)
if name.startswith('i-'):
log = get_rule_log_for_vm(name)
result.append(log)
except:
logging.debug("Failed to get rule logs, better luck next time!")
print ";".join(result)
def cleanup_rules_for_dead_vms():
return True
def cleanup_rules():
try:
chainscmd = "iptables-save | grep '^:' | awk '{print $1}' | cut -d':' -f2"
chains = execute(chainscmd).split('\n')
cleaned = 0
cleanup = []
for chain in chains:
if 1 in [ chain.startswith(c) for c in ['r-', 'i-', 's-', 'v-'] ]:
if chain.startswith('i-') or chain.startswith('r-'):
vm_name = chain + '-untagged'
else:
vm_name = chain
cmd = "virsh list |grep " + vm_name
try:
result = execute(cmd)
except:
result = None
if result == None or len(result) == 0:
logging.debug("chain " + chain + " does not correspond to a vm, cleaning up")
cleanup.append(vm_name)
continue
if result.find("running") == -1:
logging.debug("vm " + vm_name + " is not running, cleaning up")
cleanup.append(vm_name)
for vmname in cleanup:
destroy_network_rules_for_vm({'vmName':vmname})
logging.debug("Cleaned up rules for " + str(len(cleanup)) + " chains")
except:
logging.debug("Failed to cleanup rules !")
def check_rule_log_for_vm(vmName, vmId, vmIP, domID, signature, seqno):
vm_name = vmName;
logfilename = "/var/run/cloud/" + vm_name +".log"
if not os.path.exists(logfilename):
return [True, True, True, True, True, True]
try:
lines = (line.rstrip() for line in open(logfilename))
except:
logging.debug("failed to open " + logfilename)
return False
[_vmName,_vmID,_vmIP,_domID,_signature,_seqno] = ['_', '-1', '_', '-1', '_', '-1']
try:
for line in lines:
[_vmName,_vmID,_vmIP,_domID,_signature,_seqno] = line.split(',')
break
except:
logging.debug("Failed to parse log file for vm " + vm_name)
remove_rule_log_for_vm(vm_name)
return False
return [(vm_name != _vmName), (vmId != _vmID), (vmIP != _vmIP), (domID != _domID), (signature != _signature),(seqno != _seqno)]
def write_rule_log_for_vm(vmName, vmID, vmIP, domID, signature, seqno):
vm_name = vmName
logfilename = "/var/run/cloud/" + vm_name +".log"
logging.debug("Writing log to " + logfilename)
logf = open(logfilename, 'w')
output = ','.join([vmName, vmID, vmIP, domID, signature, seqno])
result = True
try:
logf.write(output)
logf.write('\n')
except:
logging.debug("Failed to write to rule log file " + logfilename)
result = False
logf.close()
return result
def remove_rule_log_for_vm(vmName):
vm_name = vmName
logfilename = "/var/run/cloud/" + vm_name +".log"
result = True
try:
os.remove(logfilename)
except:
logging.debug("Failed to delete rule log file " + logfilename)
result = False
return result
def add_network_rules(vm_name, vm_id, vm_ip, signature, seqno, vmMac, rules):
try:
vmName = vm_name
domId = getvmId(vmName)
vm_name = '-'.join(vm_name.split('-')[:-1])
vmchain = vm_name
changes = check_rule_log_for_vm(vmName, vm_id, vm_ip, domId, signature, seqno)
if not 1 in changes:
logging.debug("Rules already programmed for vm " + vm_name)
return 'true'
if changes[1] or changes[2] or changes[3]:
logging.debug("Change detected in vmId or vmIp or domId, resetting default rules")
default_network_rules(vmName, vm_ip, vm_id, vmMac)
lines = rules.split(';')
print lines
logging.debug(" programming network rules for IP: " + vm_ip + " vmname=" + vm_name)
#iptables('-F', vmchain)
print lines
for line in lines:
tokens = line.split(':')
if len(tokens) != 4:
continue
protocol = tokens[0]
start = tokens[1]
end = tokens[2]
cidrs = tokens.pop();
ips = cidrs.split(",")
ips.pop()
allow_any = False
if '0.0.0.0/0' in ips:
i = ips.index('0.0.0.0/0')
del ips[i]
allow_any = True
range = start + ":" + end
if ips:
if protocol == 'all':
for ip in ips:
iptables = "iptables -I " + vmchain + " -m state --state NEW -m iprange --src-range " + ip + " -j ACCEPT"
elif protocol != 'icmp':
for ip in ips:
iptables = "iptables -I " + vmchain + " -p " + protocol + " -m " + protocol + " --dport " + range + " -m state --state NEW -m iprange --src-range " + ip + " -j ACCEPT"
else:
range = start + "/" + end
if start == "-1":
range = "any"
for ip in ips:
iptables = "iptables -I " + vmchain + " -p icmp --icmp-type " + range + " -m iprange --src-range " + ip + " -j ACCEPT"
execute(iptables)
if allow_any and protocol != 'all':
if protocol != 'icmp':
iptables = "iptables -I " + vmchain + " -p " + protocol + " -m " + protocol + " --dport " + range + " -m state --state NEW -j ACCEPT"
else:
range = start + "/" + end
if start == "-1":
range = "any"
iptables = "iptables -I " + vmchain + " -p icmp --icmp-type " + range + " -j ACCEPT"
execute(iptables)
iptables = "iptables -A " + vmchain + " -j DROP"
execute(iptables)
if write_rule_log_for_vm(vmName, vm_id, vm_ip, domId, signature, seqno) == False:
return 'false'
return 'true'
except:
logging.debug("Failed to network rule !: " + sys.exc_type)
def getVifs(vmName):
vifs = []
try:
xmlfile = virsh("dumpxml", vmName).stdout
except:
return vifs
dom = xml.dom.minidom.parseString(xmlfile)
vifs = []
for network in dom.getElementsByTagName("interface"):
target = network.getElementsByTagName('target')[0]
nicdev = target.getAttribute("dev").strip()
vifs.append(nicdev)
return vifs
def getvmId(vmName):
cmd = "virsh list |grep " + vmName + " | awk '{print $1}'"
return bash("-c", cmd).stdout.strip()
if __name__ == '__main__':
logging.basicConfig(filename="/var/log/cloud/security_group.log", format="%(asctime)s - %(message)s", level=logging.DEBUG)
parser = OptionParser()
parser.add_option("--vmname", dest="vmName")
parser.add_option("--vmip", dest="vmIP")
parser.add_option("--vmid", dest="vmID")
parser.add_option("--vmmac", dest="vmMAC")
parser.add_option("--vif", dest="vif")
parser.add_option("--sig", dest="sig")
parser.add_option("--seq", dest="seq")
parser.add_option("--rules", dest="rules")
(option, args) = parser.parse_args()
cmd = args[0]
if cmd == "can_bridge_firewall":
can_bridge_firewall(args[1])
elif cmd == "default_network_rules":
default_network_rules(option.vmName, option.vmIP, option.vmID, option.vmMAC)
elif cmd == "destroy_network_rules_for_vm":
destroy_network_rules_for_vm(option.vmName)
elif cmd == "default_network_rules_systemvm":
default_network_rules_systemvm(option.vmName)
elif cmd == "get_rule_logs_for_vms":
get_rule_logs_for_vms()
elif cmd == "add_network_rules":
add_network_rules(option.vmName, option.vmID, option.vmIP, option.sig, option.seq, option.vmMAC, option.rules)