This is an automated email from the ASF dual-hosted git repository. gabriel pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/master by this push: new ac73e7e kvm: Security Group enhancements and refactor old code (#3113) ac73e7e is described below commit ac73e7e671ba107830f96b9fb534eb716956e405 Author: Wido den Hollander <w...@widodh.nl> AuthorDate: Wed Jan 16 16:35:18 2019 +0100 kvm: Security Group enhancements and refactor old code (#3113) * security group: Replace deprecated optparse by argparse Starting with Python 2.7 the library optparse has been replaced by argpase. This commit replaces the use of optparse by argparse Signed-off-by: Wido den Hollander <w...@widodh.nl> * security group: Remove LXC support from security_group.py LXC does not work and has been partially removed from CloudStack already Signed-off-by: Wido den Hollander <w...@widodh.nl> * security group: Refactor libvirt code Use a single function which properly throws an Exception when the connection to libvirt fails. Also simplify some logic, make it PEP-8 compatible and remove a unused function from the code. Signed-off-by: Wido den Hollander <w...@widodh.nl> * security group: Raise Exception on execute() failure If the executed command exists with a non-zero exit status we should still return the output to the command, but also raise an Exception. Signed-off-by: Wido den Hollander <w...@widodh.nl> * security group: Use a function to determin the physical device of a bridge We can not safely assume that the first device listed under a bridge is the physical device. With VXLAN isolation a vnet device can be attached to a bridge prior to the vxlanXXXX device being attached. We need to filter out those devices and then fetch the physical device attached to the bridge. In addition use the 'bridge' command instead of 'brctl'. 'bridge' is part of the iproute2 utils just like 'ip' and should be considered as the new default. This command is also available on EL6 and does not break any backwards compat. Signed-off-by: Wido den Hollander <w...@widodh.nl> * security group: --set is deprecated, use --match-set These messages are seen in the KVM Agent log: --set option deprecated, please use --match-set Functionality does not change Signed-off-by: Wido den Hollander <w...@widodh.nl> * security group: PEP-8 and indentation fixes There were a lot of styling problems in the code: - Missing whitespace or exess whitespace - CaMelCaSe function names and variables - 2-space indentation instead of 4 spaces This commit addresses those issues. Signed-off-by: Wido den Hollander <w...@widodh.nl> --- .../kvm/resource/LibvirtComputingResource.java | 2 +- scripts/vm/network/security_group.py | 467 ++++++++++----------- 2 files changed, 216 insertions(+), 253 deletions(-) diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index ced37c9..b21cd78 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -3491,7 +3491,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv private boolean canBridgeFirewall(final String prvNic) { final Script cmd = new Script(_securityGroupPath, _timeout, s_logger); cmd.add("can_bridge_firewall"); - cmd.add(prvNic); + cmd.add("--privnic", prvNic); final String result = cmd.execute(); if (result != null) { return false; diff --git a/scripts/vm/network/security_group.py b/scripts/vm/network/security_group.py index 12afe0b..2b81c3f 100755 --- a/scripts/vm/network/security_group.py +++ b/scripts/vm/network/security_group.py @@ -16,14 +16,12 @@ # specific language governing permissions and limitations # under the License. -import cloud_utils +import argparse from subprocess import check_output, CalledProcessError -from cloudutils.configFileOps import configFileOps import logging import sys import os import xml.dom.minidom -from optparse import OptionParser, OptionGroup, OptParseError, BadOptionError, OptionError, OptionConflictError, OptionValueError import re import libvirt import fcntl @@ -35,11 +33,6 @@ from netaddr.core import AddrFormatError logpath = "/var/run/cloud/" # FIXME: Logs should reside in /var/log/cloud lock_file = "/var/lock/cloudstack_security_group.lock" driver = "qemu:///system" -cfo = configFileOps("/etc/cloudstack/agent/agent.properties") -hyper = cfo.getEntry("hypervisor.type") -if hyper == "lxc": - driver = "lxc:///" - lock_handle = None @@ -61,7 +54,8 @@ def execute(cmd): try: return check_output(cmd, shell=True) except CalledProcessError as e: - pass + logging.exception('Command exited non-zero: %s', cmd) + raise def can_bridge_firewall(privnic): @@ -87,22 +81,27 @@ def can_bridge_firewall(privnic): return True +def get_libvirt_connection(): + conn = libvirt.openReadOnly(driver) + if not conn: + raise Exception('Failed to open connection to the hypervisor') + + return conn + + def virshlist(states): - libvirt_states={ 'running' : libvirt.VIR_DOMAIN_RUNNING, - 'shutoff' : libvirt.VIR_DOMAIN_SHUTOFF, - 'shutdown' : libvirt.VIR_DOMAIN_SHUTDOWN, - 'paused' : libvirt.VIR_DOMAIN_PAUSED, - 'nostate' : libvirt.VIR_DOMAIN_NOSTATE, - 'blocked' : libvirt.VIR_DOMAIN_BLOCKED, - 'crashed' : libvirt.VIR_DOMAIN_CRASHED, + libvirt_states={ 'running': libvirt.VIR_DOMAIN_RUNNING, + 'shutoff': libvirt.VIR_DOMAIN_SHUTOFF, + 'shutdown': libvirt.VIR_DOMAIN_SHUTDOWN, + 'paused': libvirt.VIR_DOMAIN_PAUSED, + 'nostate': libvirt.VIR_DOMAIN_NOSTATE, + 'blocked': libvirt.VIR_DOMAIN_BLOCKED, + 'crashed': libvirt.VIR_DOMAIN_CRASHED, } searchstates = list(libvirt_states[state] for state in states) - conn = libvirt.openReadOnly(driver) - if not conn: - print('Failed to open connection to the hypervisor') - sys.exit(3) + conn = get_libvirt_connection() alldomains = map(conn.lookupByID, conn.listDomainsID()) alldomains += map(conn.lookupByName, conn.listDefinedDomains()) @@ -117,48 +116,15 @@ def virshlist(states): return domains -def virshdomstate(domain): - libvirt_states={ libvirt.VIR_DOMAIN_RUNNING : 'running', - libvirt.VIR_DOMAIN_SHUTOFF : 'shut off', - libvirt.VIR_DOMAIN_SHUTDOWN : 'shut down', - libvirt.VIR_DOMAIN_PAUSED : 'paused', - libvirt.VIR_DOMAIN_NOSTATE : 'no state', - libvirt.VIR_DOMAIN_BLOCKED : 'blocked', - libvirt.VIR_DOMAIN_CRASHED : 'crashed', - } - - conn = libvirt.openReadOnly(driver) - if not conn: - print('Failed to open connection to the hypervisor') - sys.exit(3) - - try: - dom = (conn.lookupByName (domain)) - except libvirt.libvirtError: - return None - - state = libvirt_states[dom.info()[0]] - conn.close() - - return state - - def virshdumpxml(domain): - conn = libvirt.openReadOnly(driver) - if not conn: - print('Failed to open connection to the hypervisor') - sys.exit(3) - try: - dom = (conn.lookupByName (domain)) + conn = get_libvirt_connection() + dom = conn.lookupByName(domain) + conn.close() + return dom.XMLDesc(0) except libvirt.libvirtError: return None - xml = dom.XMLDesc(0) - conn.close() - - return xml - def ipv6_link_local_addr(mac=None): eui64 = re.sub(r'[.:-]', '', mac).lower() @@ -182,6 +148,11 @@ def split_ips_by_family(ips): return ip4s, ip6s +def get_bridge_physdev(brname): + physdev = execute("bridge -o link show | awk '/master %s / && !/^[0-9]+: vnet/ {print $2}'" % brname) + return physdev.strip() + + def destroy_network_rules_for_vm(vm_name, vif=None): vmchain = iptables_chain_name(vm_name) vmchain_egress = egress_chain_name(vm_name) @@ -229,14 +200,14 @@ def destroy_network_rules_for_vm(vm_name, vif=None): remove_rule_log_for_vm(vm_name) remove_secip_log_for_vm(vm_name) - if 1 in [ vm_name.startswith(c) for c in ['r-', 's-', 'v-'] ]: + if 1 in [vm_name.startswith(c) for c in ['r-', 's-', 'v-']]: return True return True def destroy_ebtables_rules(vm_name, vif): - eb_vm_chain=ebtables_chain_name(vm_name) + eb_vm_chain = ebtables_chain_name(vm_name) delcmd = "ebtables -t nat -L PREROUTING | grep " + eb_vm_chain delcmds = [] try: @@ -319,8 +290,8 @@ def default_ebtables_rules(vm_name, vm_ip, vm_mac, vif): def default_network_rules_systemvm(vm_name, localbrname): - bridges = getBridges(vm_name) - domid = getvmId(vm_name) + bridges = get_bridges(vm_name) + domid = get_vm_id(vm_name) vmchain = iptables_chain_name(vm_name) delete_rules_for_vm_in_bridge_firewall_chain(vm_name) @@ -332,10 +303,10 @@ def default_network_rules_systemvm(vm_name, localbrname): for bridge in bridges: if bridge != localbrname: - if not addFWFramework(bridge): + if not add_fw_framework(bridge): return False - brfw = getBrfw(bridge) - vifs = getVifsForBridge(vm_name, bridge) + brfw = get_br_fw(bridge) + vifs = get_vifs_for_bridge(vm_name, bridge) for vif in vifs: try: execute("iptables -A " + brfw + "-OUT" + " -m physdev --physdev-is-bridged --physdev-out " + vif + " -j " + vmchain) @@ -417,8 +388,6 @@ def network_rules_vmSecondaryIp(vm_name, ip_secondary, action): logging.debug("vmName = "+ vm_name) logging.debug("action = "+ action) - domid = getvmId(vm_name) - vmchain = vm_name vmchain6 = vmchain + '-6' @@ -455,12 +424,12 @@ def ebtables_rules_vmip (vmname, ips, action): def default_network_rules(vm_name, vm_id, vm_ip, vm_ip6, vm_mac, vif, brname, sec_ips): - if not addFWFramework(brname): + if not add_fw_framework(brname): return False vmName = vm_name - brfw = getBrfw(brname) - domID = getvmId(vm_name) + brfw = get_br_fw(brname) + domID = get_vm_id(vm_name) delete_rules_for_vm_in_bridge_firewall_chain(vmName) vmchain = iptables_chain_name(vm_name) vmchain_egress = egress_chain_name(vm_name) @@ -486,13 +455,13 @@ def default_network_rules(vm_name, vm_id, vm_ip, vm_ip6, vm_mac, vif, brname, se #create ipset and add vm ips to that ip set if not create_ipset_forvm(vmipsetName): - logging.debug(" failed to create ipset for rule " + str(tokens)) - return False + logging.debug("failed to create ipset for rule %s", vmipsetName) + return False #add primary nic ip to ipset if not add_to_ipset(vmipsetName, [vm_ip], action ): - logging.debug(" failed to add vm " + vm_ip + " ip to set ") - return False + logging.debug("failed to add vm " + vm_ip + " ip to set ") + return False #add secodnary nic ips to ipset secIpSet = "1" @@ -510,7 +479,7 @@ def default_network_rules(vm_name, vm_id, vm_ip, vm_ip6, vm_mac, vif, brname, se add_to_ipset(vmipsetName, ip4s, action) - if write_secip_log_for_vm(vm_name, sec_ips, vm_id) == False: + if not write_secip_log_for_vm(vm_name, sec_ips, vm_id): logging.debug("Failed to log default network rules, ignoring") try: @@ -523,10 +492,10 @@ def default_network_rules(vm_name, vm_id, vm_ip, vm_ip6, vm_mac, vif, brname, se #don't let vm spoof its ip address if vm_ip: - execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -m set ! --set " + vmipsetName + " src -j DROP") - execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -m set --set " + vmipsetName + " src -p udp --dport 53 -j RETURN ") - execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -m set --set " + vmipsetName + " src -p tcp --dport 53 -j RETURN ") - execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -m set --set " + vmipsetName + " src -j " + vmchain_egress) + execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -m set ! --match-set " + vmipsetName + " src -j DROP") + execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -m set --match-set " + vmipsetName + " src -p udp --dport 53 -j RETURN ") + execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -m set --match-set " + vmipsetName + " src -p tcp --dport 53 -j RETURN ") + execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-in " + vif + " -m set --match-set " + vmipsetName + " src -j " + vmchain_egress) execute("iptables -A " + vmchain_default + " -m physdev --physdev-is-bridged --physdev-out " + vif + " -j " + vmchain) execute("iptables -A " + vmchain + " -j DROP") except: @@ -542,8 +511,8 @@ def default_network_rules(vm_name, vm_id, vm_ip, vm_ip6, vm_mac, vif, brname, se logging.debug("Failed to log default network rules, ignoring") if not create_ipset_forvm(vmipsetName6, family='inet6', type='hash:net'): - logging.debug(" failed to create ivp6 ipset for rule " + str(tokens)) - return False + logging.debug(" failed to create ivp6 ipset for rule " + str(tokens)) + return False vm_ip6_addr = [ipv6_link_local] try: @@ -622,7 +591,7 @@ def post_default_network_rules(vm_name, vm_id, vm_ip, vm_mac, vif, brname, dhcpS iptables_vmchain=iptables_chain_name(vm_name) vmchain_in = iptables_vmchain + "-in" vmchain_out = iptables_vmchain + "-out" - domID = getvmId(vm_name) + domID = get_vm_id(vm_name) try: execute("iptables -I " + vmchain_default + " 4 -m physdev --physdev-is-bridged --physdev-in " + vif + " --source " + vm_ip + " -j ACCEPT") except: @@ -705,7 +674,7 @@ def get_rule_log_for_vm(vmName): def check_domid_changed(vmName): - curr_domid = getvmId(vmName) + curr_domid = get_vm_id(vmName) if (curr_domid is None) or (not curr_domid.isdigit()): curr_domid = '-1' @@ -754,13 +723,13 @@ def network_rules_for_rebooted_vm(vmName): vmchain = iptables_chain_name(vm_name) vmchain_default = '-'.join(vmchain.split('-')[:-1]) + "-def" - vifs = getVifs(vmName) + vifs = get_vifs(vmName) logging.debug(vifs, brName) for v in vifs: - execute("iptables -A " + getBrfw(brName) + "-IN " + " -m physdev --physdev-is-bridged --physdev-in " + v + " -j "+ vmchain_default) - execute("iptables -A " + getBrfw(brName) + "-OUT " + " -m physdev --physdev-is-bridged --physdev-out " + v + " -j "+ vmchain_default) - execute("ip6tables -A " + getBrfw(brName) + "-IN " + " -m physdev --physdev-is-bridged --physdev-in " + v + " -j " + vmchain_default) - execute("ip6tables -A " + getBrfw(brName) + "-OUT " + " -m physdev --physdev-is-bridged --physdev-out " + v + " -j " + vmchain_default) + execute("iptables -A " + get_br_fw(brName) + "-IN " + " -m physdev --physdev-is-bridged --physdev-in " + v + " -j " + vmchain_default) + execute("iptables -A " + get_br_fw(brName) + "-OUT " + " -m physdev --physdev-is-bridged --physdev-out " + v + " -j " + vmchain_default) + execute("ip6tables -A " + get_br_fw(brName) + "-IN " + " -m physdev --physdev-is-bridged --physdev-in " + v + " -j " + vmchain_default) + execute("ip6tables -A " + get_br_fw(brName) + "-OUT " + " -m physdev --physdev-is-bridged --physdev-out " + v + " -j " + vmchain_default) #change antispoof rule in vmchain try: @@ -787,14 +756,14 @@ def network_rules_for_rebooted_vm(vmName): def get_rule_logs_for_vms(): - state=['running'] + state = ['running'] vms = virshlist(state) result = [] try: for name in vms: name = name.rstrip() - if 1 not in [ name.startswith(c) for c in ['r-', 's-', 'v-', 'i-'] ]: + if 1 not in [name.startswith(c) for c in ['r-', 's-', 'v-', 'i-'] ]: continue network_rules_for_rebooted_vm(name) if name.startswith('i-'): @@ -811,7 +780,7 @@ def cleanup_rules_for_dead_vms(): def cleanup_bridge(bridge): - bridge_name = getBrfw(bridge) + bridge_name = get_br_fw(bridge) logging.debug("Cleaning old bridge chains: " + bridge_name) if not bridge_name: return True @@ -840,7 +809,7 @@ def cleanup_bridge(bridge): def cleanup_rules(): try: - states=['running','paused'] + states = ['running', 'paused'] vmsInHost = virshlist(states) logging.debug(" Vms on the host : %s ", vmsInHost) @@ -854,11 +823,11 @@ def cleanup_rules(): for chain in chains: if 1 in [ chain.startswith(c) for c in ['r-', 'i-', 's-', 'v-'] ]: vm_name = chain - vmpresent=False + vmpresent = False for vm in vmsInHost: if vm_name in vm: - vmpresent=True + vmpresent = True break if vmpresent is False: @@ -873,12 +842,12 @@ def cleanup_rules(): logging.debug(" ebtables chains in the host: %s ", chains) for chain in filter(None, chains): - if 1 in [ chain.startswith(c) for c in ['r-', 'i-', 's-', 'v-'] ]: + if 1 in [chain.startswith(c) for c in ['r-', 'i-', 's-', 'v-']]: vm_name = chain - vmpresent=False + vmpresent = False for vm in vmsInHost: if vm_name in vm: - vmpresent=True + vmpresent = True break if vmpresent is False: @@ -980,144 +949,144 @@ def egress_chain_name(vm_name): def parse_network_rules(rules): - ret = [] + ret = [] - if rules is None or len(rules) == 0: - return ret + if rules is None or len(rules) == 0: + return ret - lines = rules.split('NEXT;')[:-1] - for line in lines: - tokens = line.split(';', 3) - if len(tokens) != 4: - continue + lines = rules.split('NEXT;')[:-1] + for line in lines: + tokens = line.split(';', 3) + if len(tokens) != 4: + continue - ruletype, protocol = tokens[0].split(':') - start = int(tokens[1]) - end = int(tokens[2]) - cidrs = tokens.pop() + ruletype, protocol = tokens[0].split(':') + start = int(tokens[1]) + end = int(tokens[2]) + cidrs = tokens.pop() - ipv4 = [] - ipv6 = [] - for ip in cidrs.split(","): - try: - network = IPNetwork(ip) - if network.version == 4: - ipv4.append(ip) - else: - ipv6.append(ip) - except: - pass + ipv4 = [] + ipv6 = [] + for ip in cidrs.split(","): + try: + network = IPNetwork(ip) + if network.version == 4: + ipv4.append(ip) + else: + ipv6.append(ip) + except: + pass - ret.append({'ipv4': ipv4, 'ipv6': ipv6, 'ruletype': ruletype, - 'start': start, 'end': end, 'protocol': protocol}) + ret.append({'ipv4': ipv4, 'ipv6': ipv6, 'ruletype': ruletype, + 'start': start, 'end': end, 'protocol': protocol}) - return ret + return ret def add_network_rules(vm_name, vm_id, vm_ip, vm_ip6, signature, seqno, vmMac, rules, vif, brname, sec_ips): - try: - vmName = vm_name - domId = getvmId(vmName) + try: + vmName = vm_name + domId = get_vm_id(vmName) - changes = check_rule_log_for_vm(vmName, vm_id, vm_ip, domId, signature, seqno) + 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 not 1 in changes: + logging.debug("Rules already programmed for vm " + vm_name) + return True - if changes[0] or changes[1] or changes[2] or changes[3]: - default_network_rules(vmName, vm_id, vm_ip, vm_ip6, vmMac, vif, brname, sec_ips) + if changes[0] or changes[1] or changes[2] or changes[3]: + default_network_rules(vmName, vm_id, vm_ip, vm_ip6, vmMac, vif, brname, sec_ips) - logging.debug(" programming network rules for IP: " + vm_ip + " vmname=" + vm_name) + logging.debug("programming network rules for IP: " + vm_ip + " vmname=%s", vm_name) - egress_chain_name(vm_name) - vmchain = iptables_chain_name(vm_name) - egress_vmchain = egress_chain_name(vm_name) + egress_chain_name(vm_name) + vmchain = iptables_chain_name(vm_name) + egress_vmchain = egress_chain_name(vm_name) - try: - for chain in [vmchain, egress_vmchain]: - execute('iptables -F ' + chain) - execute('ip6tables -F ' + chain) - except: - logging.debug("Error flushing iptables rules for " + vm_name + ". Presuming firewall rules deleted, re-initializing." ) - default_network_rules(vm_name, vm_id, vm_ip, vm_ip6, vmMac, vif, brname, sec_ips) - - egressrule_v4 = 0 - egressrule_v6 = 0 + try: + for chain in [vmchain, egress_vmchain]: + execute('iptables -F ' + chain) + execute('ip6tables -F ' + chain) + except: + logging.debug("Error flushing iptables rules for " + vm_name + ". Presuming firewall rules deleted, re-initializing." ) + default_network_rules(vm_name, vm_id, vm_ip, vm_ip6, vmMac, vif, brname, sec_ips) - for rule in parse_network_rules(rules): - start = rule['start'] - end = rule['end'] - protocol = rule['protocol'] + egressrule_v4 = 0 + egressrule_v6 = 0 - if rule['ruletype'] == 'E': - vmchain = egress_vmchain - direction = "-d" - action = "RETURN" - if rule['ipv4']: - egressrule_v4 =+ 1 + for rule in parse_network_rules(rules): + start = rule['start'] + end = rule['end'] + protocol = rule['protocol'] - if rule['ipv6']: - egressrule_v6 +=1 + if rule['ruletype'] == 'E': + vmchain = egress_vmchain + direction = "-d" + action = "RETURN" + if rule['ipv4']: + egressrule_v4 =+ 1 - else: - vmchain = vm_name - action = "ACCEPT" - direction = "-s" - - range = str(start) + ':' + str(end) - if 'icmp' == protocol: - range = str(start) + '/' + str(end) - if start == -1: - range = 'any' - - for ip in rule['ipv4']: - if protocol == 'all': - execute('iptables -I ' + vmchain + ' -m state --state NEW ' + direction + ' ' + ip + ' -j ' + action) - elif protocol != 'icmp': - execute('iptables -I ' + vmchain + ' -p ' + protocol + ' -m ' + protocol + ' --dport ' + range + ' -m state --state NEW ' + direction + ' ' + ip + ' -j ' + action) - else: - execute('iptables -I ' + vmchain + ' -p icmp --icmp-type ' + range + ' ' + direction + ' ' + ip + ' -j ' + action) + if rule['ipv6']: + egressrule_v6 +=1 - for ip in rule['ipv6']: - if protocol == 'all': - execute('ip6tables -I ' + vmchain + ' -m state --state NEW ' + direction + ' ' + ip + ' -j ' + action) - elif 'icmp' != protocol: - execute('ip6tables -I ' + vmchain + ' -p ' + protocol + ' -m ' + protocol + ' --dport ' + range + ' -m state --state NEW ' + direction + ' ' + ip + ' -j ' + action) else: - # ip6tables does not allow '--icmpv6-type any', allowing all ICMPv6 is done by not allowing a specific type - if range == 'any': - execute('ip6tables -I ' + vmchain + ' -p icmpv6 ' + direction + ' ' + ip + ' -j ' + action) + vmchain = vm_name + action = "ACCEPT" + direction = "-s" + + range = str(start) + ':' + str(end) + if 'icmp' == protocol: + range = str(start) + '/' + str(end) + if start == -1: + range = 'any' + + for ip in rule['ipv4']: + if protocol == 'all': + execute('iptables -I ' + vmchain + ' -m state --state NEW ' + direction + ' ' + ip + ' -j ' + action) + elif protocol != 'icmp': + execute('iptables -I ' + vmchain + ' -p ' + protocol + ' -m ' + protocol + ' --dport ' + range + ' -m state --state NEW ' + direction + ' ' + ip + ' -j ' + action) else: - execute('ip6tables -I ' + vmchain + ' -p icmpv6 --icmpv6-type ' + range + ' ' + direction + ' ' + ip + ' -j ' + action) + execute('iptables -I ' + vmchain + ' -p icmp --icmp-type ' + range + ' ' + direction + ' ' + ip + ' -j ' + action) - egress_vmchain = egress_chain_name(vm_name) - if egressrule_v4 == 0 : - execute('iptables -A ' + egress_vmchain + ' -j RETURN') - else: - execute('iptables -A ' + egress_vmchain + ' -j DROP') + for ip in rule['ipv6']: + if protocol == 'all': + execute('ip6tables -I ' + vmchain + ' -m state --state NEW ' + direction + ' ' + ip + ' -j ' + action) + elif 'icmp' != protocol: + execute('ip6tables -I ' + vmchain + ' -p ' + protocol + ' -m ' + protocol + ' --dport ' + range + ' -m state --state NEW ' + direction + ' ' + ip + ' -j ' + action) + else: + # ip6tables does not allow '--icmpv6-type any', allowing all ICMPv6 is done by not allowing a specific type + if range == 'any': + execute('ip6tables -I ' + vmchain + ' -p icmpv6 ' + direction + ' ' + ip + ' -j ' + action) + else: + execute('ip6tables -I ' + vmchain + ' -p icmpv6 --icmpv6-type ' + range + ' ' + direction + ' ' + ip + ' -j ' + action) + + egress_vmchain = egress_chain_name(vm_name) + if egressrule_v4 == 0 : + execute('iptables -A ' + egress_vmchain + ' -j RETURN') + else: + execute('iptables -A ' + egress_vmchain + ' -j DROP') - if egressrule_v6 == 0 : - execute('ip6tables -A ' + egress_vmchain + ' -j RETURN') - else: - execute('ip6tables -A ' + egress_vmchain + ' -j DROP') + if egressrule_v6 == 0 : + execute('ip6tables -A ' + egress_vmchain + ' -j RETURN') + else: + execute('ip6tables -A ' + egress_vmchain + ' -j DROP') - vmchain = iptables_chain_name(vm_name) + vmchain = iptables_chain_name(vm_name) - execute('iptables -A ' + vmchain + ' -j DROP') - execute('ip6tables -A ' + vmchain + ' -j DROP') + execute('iptables -A ' + vmchain + ' -j DROP') + execute('ip6tables -A ' + vmchain + ' -j DROP') - if not write_rule_log_for_vm(vmName, vm_id, vm_ip, domId, signature, seqno): - return False + if not write_rule_log_for_vm(vmName, vm_id, vm_ip, domId, signature, seqno): + return False - return True - except: - logging.exception("Failed to network rule !") + return True + except: + logging.exception("Failed to network rule !") -def getVifs(vmName): +def get_vifs(vm_name): vifs = [] - xmlfile = virshdumpxml(vmName) + xmlfile = virshdumpxml(vm_name) if not xmlfile: return vifs @@ -1129,9 +1098,9 @@ def getVifs(vmName): return vifs -def getVifsForBridge(vmName, brname): +def get_vifs_for_bridge(vm_name, brname): vifs = [] - xmlfile = virshdumpxml(vmName) + xmlfile = virshdumpxml(vm_name) if not xmlfile: return vifs @@ -1146,9 +1115,9 @@ def getVifsForBridge(vmName, brname): return list(set(vifs)) -def getBridges(vmName): +def get_bridges(vm_name): bridges = [] - xmlfile = virshdumpxml(vmName) + xmlfile = virshdumpxml(vm_name) if not xmlfile: return bridges @@ -1160,15 +1129,10 @@ def getBridges(vmName): return list(set(bridges)) -def getvmId(vmName): - - conn = libvirt.openReadOnly(driver) - if not conn: - print('Failed to open connection to the hypervisor') - sys.exit(3) - +def get_vm_id(vm_name): + conn = get_libvirt_connection() try: - dom = (conn.lookupByName (vmName)) + dom = (conn.lookupByName (vm_name)) except libvirt.libvirtError: return None @@ -1180,7 +1144,7 @@ def getvmId(vmName): return res -def getBrfw(brname): +def get_br_fw(brname): cmd = "iptables-save |grep physdev-is-bridged |grep FORWARD |grep BF |grep '\-o' | grep -w " + brname + "|awk '{print $9}' | head -1" brfwname = execute(cmd).strip() if not brfwname: @@ -1188,7 +1152,7 @@ def getBrfw(brname): return brfwname -def addFWFramework(brname): +def add_fw_framework(brname): try: execute("sysctl -w net.bridge.bridge-nf-call-arptables=1") execute("sysctl -w net.bridge.bridge-nf-call-iptables=1") @@ -1196,7 +1160,7 @@ def addFWFramework(brname): except: logging.warn("failed to turn on bridge netfilter") - brfw = getBrfw(brname) + brfw = get_br_fw(brname) try: execute("iptables -L " + brfw) except: @@ -1231,6 +1195,8 @@ def addFWFramework(brname): except: execute('ip6tables -N ' + brfwin) + physdev = get_bridge_physdev(brname) + try: refs = int(execute("""iptables -n -L %s | awk '/%s(.*)references/ {gsub(/\(/, "") ;print $3}'""" % (brfw,brfw)).strip()) refs6 = int(execute("""ip6tables -n -L %s | awk '/%s(.*)references/ {gsub(/\(/, "") ;print $3}'""" % (brfw,brfw)).strip()) @@ -1240,22 +1206,20 @@ def addFWFramework(brname): execute("iptables -I FORWARD -o " + brname + " -j DROP") execute("iptables -I FORWARD -i " + brname + " -m physdev --physdev-is-bridged -j " + brfw) execute("iptables -I FORWARD -o " + brname + " -m physdev --physdev-is-bridged -j " + brfw) - phydev = execute("brctl show | awk '/^%s[ \t]/ {print $4}'" % brname ).strip() execute("iptables -A " + brfw + " -m state --state RELATED,ESTABLISHED -j ACCEPT") execute("iptables -A " + brfw + " -m physdev --physdev-is-bridged --physdev-is-in -j " + brfwin) execute("iptables -A " + brfw + " -m physdev --physdev-is-bridged --physdev-is-out -j " + brfwout) - execute("iptables -A " + brfw + " -m physdev --physdev-is-bridged --physdev-out " + phydev + " -j ACCEPT") + execute("iptables -A " + brfw + " -m physdev --physdev-is-bridged --physdev-out " + physdev + " -j ACCEPT") if refs6 == 0: execute('ip6tables -I FORWARD -i ' + brname + ' -j DROP') execute('ip6tables -I FORWARD -o ' + brname + ' -j DROP') execute('ip6tables -I FORWARD -i ' + brname + ' -m physdev --physdev-is-bridged -j ' + brfw) execute('ip6tables -I FORWARD -o ' + brname + ' -m physdev --physdev-is-bridged -j ' + brfw) - phydev = execute("brctl show | awk '/^%s[ \t]/ {print $4}'" % brname ).strip() execute('ip6tables -A ' + brfw + ' -m state --state RELATED,ESTABLISHED -j ACCEPT') execute('ip6tables -A ' + brfw + ' -m physdev --physdev-is-bridged --physdev-is-in -j ' + brfwin) execute('ip6tables -A ' + brfw + ' -m physdev --physdev-is-bridged --physdev-is-out -j ' + brfwout) - execute('ip6tables -A ' + brfw + ' -m physdev --physdev-is-bridged --physdev-out ' + phydev + ' -j ACCEPT') + execute('ip6tables -A ' + brfw + ' -m physdev --physdev-is-bridged --physdev-out ' + physdev + ' -j ACCEPT') return True except: @@ -1269,29 +1233,28 @@ def addFWFramework(brname): if __name__ == '__main__': logging.basicConfig(filename="/var/log/cloudstack/agent/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("--vmip6", dest="vmIP6") - 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") - parser.add_option("--brname", dest="brname") - parser.add_option("--localbrname", dest="localbrname") - parser.add_option("--dhcpSvr", dest="dhcpSvr") - parser.add_option("--hostIp", dest="hostIp") - parser.add_option("--hostMacAddr", dest="hostMacAddr") - parser.add_option("--nicsecips", dest="nicSecIps") - parser.add_option("--action", dest="action") - (option, args) = parser.parse_args() - if len(args) == 0: - logging.debug("No command to execute") - sys.exit(1) - cmd = args[0] - logging.debug("Executing command: " + str(cmd)) + parser = argparse.ArgumentParser(description='Apache CloudStack Security Groups') + parser.add_argument("command") + parser.add_argument("--vmname", dest="vmName") + parser.add_argument("--vmip", dest="vmIP") + parser.add_argument("--vmip6", dest="vmIP6") + parser.add_argument("--vmid", dest="vmID") + parser.add_argument("--vmmac", dest="vmMAC") + parser.add_argument("--vif", dest="vif") + parser.add_argument("--sig", dest="sig") + parser.add_argument("--seq", dest="seq") + parser.add_argument("--rules", dest="rules") + parser.add_argument("--brname", dest="brname") + parser.add_argument("--localbrname", dest="localbrname") + parser.add_argument("--dhcpSvr", dest="dhcpSvr") + parser.add_argument("--hostIp", dest="hostIp") + parser.add_argument("--hostMacAddr", dest="hostMacAddr") + parser.add_argument("--nicsecips", dest="nicSecIps") + parser.add_argument("--action", dest="action") + parser.add_argument("--privnic", dest="privnic") + args = parser.parse_args() + cmd = args.command + logging.debug("Executing command: %s", cmd) for i in range(0, 30): if obtain_file_lock(lock_file) is False: @@ -1301,23 +1264,23 @@ if __name__ == '__main__': break if cmd == "can_bridge_firewall": - can_bridge_firewall(args[1]) + can_bridge_firewall(args.privnic) elif cmd == "default_network_rules": - default_network_rules(option.vmName, option.vmID, option.vmIP, option.vmIP6, option.vmMAC, option.vif, option.brname, option.nicSecIps) + default_network_rules(args.vmName, args.vmID, args.vmIP, args.vmIP6, args.vmMAC, args.vif, args.brname, args.nicSecIps) elif cmd == "destroy_network_rules_for_vm": - destroy_network_rules_for_vm(option.vmName, option.vif) + destroy_network_rules_for_vm(args.vmName, args.vif) elif cmd == "default_network_rules_systemvm": - default_network_rules_systemvm(option.vmName, option.localbrname) + default_network_rules_systemvm(args.vmName, args.localbrname) 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.vmIP6, option.sig, option.seq, option.vmMAC, option.rules, option.vif, option.brname, option.nicSecIps) + add_network_rules(args.vmName, args.vmID, args.vmIP, args.vmIP6, args.sig, args.seq, args.vmMAC, args.rules, args.vif, args.brname, args.nicSecIps) elif cmd == "network_rules_vmSecondaryIp": - network_rules_vmSecondaryIp(option.vmName, option.nicSecIps, option.action) + network_rules_vmSecondaryIp(args.vmName, args.nicSecIps, args.action) elif cmd == "cleanup_rules": cleanup_rules() elif cmd == "post_default_network_rules": - post_default_network_rules(option.vmName, option.vmID, option.vmIP, option.vmMAC, option.vif, option.brname, option.dhcpSvr, option.hostIp, option.hostMacAddr) + post_default_network_rules(args.vmName, args.vmID, args.vmIP, args.vmMAC, args.vif, args.brname, args.dhcpSvr, args.hostIp, args.hostMacAddr) else: logging.debug("Unknown command: " + cmd) sys.exit(1)