面向Cloudstack4.2 Security Group XenServer 6.x ,對於KVM原理同樣,須要和Agent通訊,調用security_group.py。 java
安全組是AWS裏的一種技術,用於隔離多租戶虛擬機,Cloudstack也支持安全組,3.0.x版本只支持Basic Zone的安全組,4.1之後引入Advance Shared Zone的安全組,目前安全組支持的Hypervisor爲KVM 和Xen系列,對於VMware可使用PVLAN也可使用vShield實現,須要獨立開發。 python
首先要建立帶安全組的Shared網絡服務,建立Zone,建立VM時,選擇新建立的安全組或默認的。若是不選擇安全組,會使用默認的安全組。默認狀況下,安全組會將VM的Deny All Ingress ,Allow ALL Egress. shell
對於XenServer< 6.1,須要安裝CSP包來支持安全組,從XenServer6.1開始,CSP已經集成到XenServer安裝盤中。 api
XenServer 6.0.2:
http://download.cloud.com/releases/3.0.1/XS-6.0.2/xenserver-cloud-supp.tgz
XenServer 5.6 SP2:
http://download.cloud.com/releases/2.2.0/xenserver-cloud-supp.tgz
XenServer 6.0:
http://download.cloud.com/releases/3.0/xenserver-cloud-supp.tgz 安全
安裝: 網絡
#tar xf xenserver-cloud-supp.tgz #xe-install-supplemental-pack xenserver-cloud-supp.iso
另外須要將網絡從OVS改成Bridge。在XenServer機器執行 session
#cat /etc/xensource/network.conf openvswitch
執行改成bridge或者手動改成bridge,並重啓主機。 app
# xe-switch-network-backend bridge
另外須要確認如下這些值,以下表示OK。 dom
#sysctl -a|grep bridge net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 0 net.bridge.bridge-nf-call-arptables = 1
打開內核轉發: oop
net.ipv4.ip_forward = 1
若是不是,修改
vi /etc/sysctl.conf #sysctl -p #生效
CitrixResourceBase.java 是負責和XenServer交互的底層資源類。在VM啓動時,若是啓用安全組,將創建默認規則(Ingress Allow,Egress Deny).
方法爲 execute(StartCommand cmd),裏面代碼片斷:
if (_canBridgeFirewall) { String result = null; if (vmSpec.getType() != VirtualMachine.Type.User) { NicTO[] nics = vmSpec.getNics(); boolean secGrpEnabled = false; for (NicTO nic : nics) { if (nic.isSecurityGroupEnabled() || (nic.getIsolationUri() != null && nic.getIsolationUri().getScheme() .equalsIgnoreCase(IsolationType.Ec2.toString()))) { secGrpEnabled = true; break; } } if (secGrpEnabled) { result = callHostPlugin(conn, "vmops", "default_network_rules_systemvm", "vmName", vmName); if (result == null || result.isEmpty() || !Boolean.parseBoolean(result)) { s_logger.warn("Failed to program default network rules for " + vmName); } else { s_logger.info("Programmed default network rules for " + vmName); } } } else { // For user vm, program the rules for each nic if the isolation // uri scheme is ec2 NicTO[] nics = vmSpec.getNics(); for (NicTO nic : nics) { if (nic.isSecurityGroupEnabled() || nic.getIsolationUri() != null && nic.getIsolationUri().getScheme().equalsIgnoreCase(IsolationType.Ec2.toString())) { List<String> nicSecIps = nic.getNicSecIps(); String secIpsStr; StringBuilder sb = new StringBuilder(); if (nicSecIps != null) { for (String ip : nicSecIps) { sb.append(ip).append(":"); } secIpsStr = sb.toString(); } else { secIpsStr = "0:"; } result = callHostPlugin(conn, "vmops", "default_network_rules", "vmName", vmName, "vmIP", nic.getIp(), "vmMAC", nic.getMac(), "vmID", Long.toString(vmSpec.getId()), "secIps", secIpsStr); if (result == null || result.isEmpty() || !Boolean.parseBoolean(result)) { s_logger.warn("Failed to program default network rules for " + vmName + " on nic with ip:" + nic.getIp() + " mac:" + nic.getMac()); } else { s_logger.info("Programmed default network rules for " + vmName + " on nic with ip:" + nic.getIp() + " mac:" + nic.getMac()); } } } } }
該方法將判斷主機網絡是否爲bridge,若是不是就不會創建安全組規則。將遍歷VM全部網卡,判斷是否啓用安全組隔離類型爲EC2,即安全組隔離。
對於System VM(vrouter,ssvm,cpvm),要單獨處理,若是是System VM,會調用主機plugin vmops裏的方法default_network_rules_systemvm 參數爲VM名字,若是是User VM,會調用default_network_rules,參數爲 vmName, vmIP ,vmMAC,, secIps,secIps表示網卡的第二個IP地址,cloudstack支持一個網卡多個IP地址。
主機Plugin vmops 位於cloudstack 代碼樹中cloudstack/scripts/vm/hypervisor/xenserver,在Xenserver主機/etc/xapi.d/plugins/
Cloudstack對於Xenserver和KVM的安全組主要使用Iptables,ebtable ,ipset,arptables實現。
def default_network_rules_systemvm(session, args): ...... vmchain = chain_name(vm_name) ....... delete_rules_for_vm_in_bridge_firewall_chain(vm_name) #創建iptables chain try: util.pread2(['iptables', '-N', vmchain]) except: util.pread2(['iptables', '-F', vmchain]) #容許全部出流量 allow_egress_traffic(session) #遍歷vif for vif in vifs: try: util.pread2(['iptables', '-A', 'BRIDGE-FIREWALL', '-m', 'physdev', '--physdev-is-bridged', '--physdev-out', vif, '-j', vmchain]) util.pread2(['iptables', '-I', 'BRIDGE-FIREWALL', '2', '-m', 'physdev', '--physdev-is-bridged', '--physdev-in', vif, '-j', vmchain]) util.pread2(['iptables', '-I', vmchain, '-m', 'physdev', '--physdev-is-bridged', '--physdev-in', vif, '-j', 'RETURN']) except: util.SMlog("Failed to program default rules") return 'false' util.pread2(['iptables', '-A', vmchain, '-j', 'ACCEPT']) return 'true'
Chain s-6-VM (8 references) pkts bytes target prot opt in out source destination 0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif18.2 --physdev-is-bridged 0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif18.0 --physdev-is-bridged 0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif18.1 --physdev-is-bridged 0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif18.3 --physdev-is-bridged 0 0 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0
Chain v-2-VM (6 references) pkts bytes target prot opt in out source destination 0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif17.2 --physdev-is-bridged 0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif17.0 --physdev-is-bridged 0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif17.1 --physdev-is-bridged 0 0 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0
Chain r-4-VM (4 references) pkts bytes target prot opt in out source destination 0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif19.0 --physdev-is-bridged 0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif19.1 --physdev-is-bridged 0 0 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0
經過以上三個SystemVM能夠看到,基本全部流量都RETURN到BRIDGE-FIREWALL處理
def default_network_rules(session, args): vm_name = args.pop('vmName') vm_ip = args.pop('vmIP') vm_id = args.pop('vmID')# vm_mac = args.pop('vmMAC') sec_ips = args.pop("secIps") action = "-A" try: vm = session.xenapi.VM.get_by_name_label(vm_name) if len(vm) != 1: util.SMlog("### Failed to get record for vm " + vm_name) return 'false' vm_rec = session.xenapi.VM.get_record(vm[0]) domid = vm_rec.get('domid') except: util.SMlog("### Failed to get domid for vm " + vm_name) return 'false' if domid == '-1': util.SMlog("### Failed to get domid for vm (-1): " + vm_name) return 'false' vif = "vif" + domid + ".0" tap = "tap" + domid + ".0" vifs = [vif] try: util.pread2(['ifconfig', tap]) vifs.append(tap) except: pass delete_rules_for_vm_in_bridge_firewall_chain(vm_name) vmchain = chain_name(vm_name) vmchain_egress = egress_chain_name(vm_name) #EGRESS vmchain_default = chain_name_def(vm_name) #INGRESS destroy_ebtables_rules(vmchain) #創建三個chain: i-x-y-VM ,i-x-y-VM-eg,i-x-y-VM-def try: 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]) except: util.pread2(['iptables', '-F', vmchain_default]) vmipset = vm_name #建立ipset if create_ipset_forvm(vmipset) == False: util.SMlog(" failed to create ipset for rule " + str(tokens)) return 'false' #添加ip到ipset if add_to_ipset(vmipset, [vm_ip], action ) == False: util.SMlog(" failed to add vm " + vm_ip + " ip to set ") return 'false' #添加第二個ip到ipset secIpSet = "1" ips = sec_ips.split(':') ips.pop() if ips[0] == "0": secIpSet = "0"; if secIpSet == "1": util.SMlog("Adding ipset for secondary ips") add_to_ipset(vmipset, ips, action) if write_secip_log_for_vm(vm_name, sec_ips, vm_id) == False: util.SMlog("Failed to log default network rules, ignoring") keyword = '--' + get_ipset_keyword() try: for v in vifs: util.pread2(['iptables', '-A', 'BRIDGE-FIREWALL', '-m', 'physdev', '--physdev-is-bridged', '--physdev-out', v, '-j', vmchain_default]) util.pread2(['iptables', '-I', 'BRIDGE-FIREWALL', '2', '-m', 'physdev', '--physdev-is-bridged', '--physdev-in', v, '-j', vmchain_default]) #don't let vm spoof its ip address for v in vifs: #容許VM的DNS請求,RETURN到上級chain處理 util.pread2(['iptables', '-A', vmchain_default, '-m', 'physdev', '--physdev-is-bridged', '--physdev-in', v, '-m', 'set', keyword, vmipset, 'src', '-p', 'udp', '--dport', '53', '-j', 'RETURN']) util.pread2(['iptables', '-A', vmchain_default, '-m', 'physdev', '--physdev-is-bridged', '--physdev-in', v, '-m', 'set', '!', keyword, vmipset, 'src', '-j', 'DROP']) util.pread2(['iptables', '-A', vmchain_default, '-m', 'physdev', '--physdev-is-bridged', '--physdev-out', v, '-m', 'set', '!', keyword, vmipset, 'dst', '-j', 'DROP']) util.pread2(['iptables', '-A', vmchain_default, '-m', 'physdev', '--physdev-is-bridged', '--physdev-in', v, '-m', 'set', keyword, vmipset, 'src', '-j', vmchain_egress]) util.pread2(['iptables', '-A', vmchain_default, '-m', 'physdev', '--physdev-is-bridged', '--physdev-out', v, '-j', vmchain]) except: util.SMlog("Failed to program default rules for vm " + vm_name) return 'false' default_arp_antispoof(vmchain, vifs, vm_ip, vm_mac) #add default arp rules for secondary ips; if secIpSet == "1": util.SMlog("Adding arp rules for sec ip") arp_rules_vmip(vmchain, vifs, ips, vm_mac, action) default_ebtables_antispoof_rules(vmchain, vifs, vm_ip, vm_mac) if write_rule_log__vm(vm_name, vm_id, vm_ip, domid, '_initial_', '-1', vm_mac) == False: util.SMlog("Failed to log default network rules, ignoring") util.SMlog("Programmed default rules for vm " + vm_name) return 'true'
Chain i-2-7-VM (1 references) target prot opt source destination DROP all -- 0.0.0.0/0 0.0.0.0/0 Chain i-2-7-VM-eg (1 references) target prot opt source destination RETURN all -- 0.0.0.0/0 0.0.0.0/0 Chain i-2-7-def (2 references) target prot opt source destination RETURN udp -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif21.0 --physdev-is-bridged set i-2-7-VM src udp dpt:53 DROP all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif21.0 --physdev-is-bridged !set i-2-7-VM src DROP all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-out vif21.0 --physdev-is-bridged !set i-2-7-VM dst i-2-7-VM-eg all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif21.0 --physdev-is-bridged set i-2-7-VM src i-2-7-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-out vif21.0 --physdev-is-bridged
對於User VM,會建立ebtables和ipset,arptable。而且容許DHCP 67/68端口,不然用戶VM將沒法自動得到IP。
Bridge chain: FORWARD, entries: 3, policy: ACCEPT -j DEFAULT_EBTABLES -i vif21.0 -j i-2-7-VM #入接口 -o vif21.0 -j i-2-7-VM #出接口 Bridge chain: OUTPUT, entries: 0, policy: ACCEPT Bridge chain: DEFAULT_EBTABLES, entries: 12, policy: ACCEPT #DHCP的廣播目的地址是255.255.255.255,源地址是0.0.0.0 -p IPv4 --ip-dst 255.255.255.255 --ip-proto udp --ip-dport 67 -j ACCEPT -p IPv4 --ip-dst 255.255.255.255 --ip-proto udp --ip-dport 68 -j ACCEPT -p ARP --arp-op Request -j ACCEPT -p ARP --arp-op Reply -j ACCEPT -p IPv4 -d Broadcast -j DROP -p IPv4 -d Multicast -j DROP -p IPv4 --ip-dst 255.255.255.255 -j DROP -p IPv4 --ip-dst 224.0.0.0/4 -j DROP -p IPv4 -j RETURN -p IPv6 -j DROP -p 802_1Q -j DROP -j DROP Bridge chain: i-2-7-VM, entries: 2, policy: ACCEPT # drop fake dhcp and snooping of dhcp requests -p IPv4 -i vif21.0 --ip-proto udp --ip-dport 68 -j DROP -p IPv4 -o vif21.0 --ip-proto udp --ip-dport 67 -j DROP
ipset是以VM名字命名的,member是VM的IP地址。
Name: i-2-7-VM Type: iphash References: 4 Header: hashsize: 1024 probes: 8 resize: 50 Members: 192.168.2xx.xxx
對於target 爲RETURN 的會交給上級BRIDGE_FIREWALL 處理。
Chain BRIDGE_FIREWALL (1 references) target prot opt source destination BRIDGE-DEFAULT-FIREWALL all -- 0.0.0.0/0 0.0.0.0/0 i-2-7-def all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif21.0 --physdev-is-bridged r-4-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif19.0 --physdev-is-bridged r-4-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif19.1 --physdev-is-bridged s-6-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif18.2 --physdev-is-bridged s-6-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif18.0 --physdev-is-bridged s-6-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif18.1 --physdev-is-bridged s-6-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif18.3 --physdev-is-bridged v-2-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif17.2 --physdev-is-bridged v-2-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif17.0 --physdev-is-bridged v-2-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vif17.1 --physdev-is-bridged v-2-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-out vif17.1 --physdev-is-bridged v-2-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-out vif17.0 --physdev-is-bridged v-2-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-out vif17.2 --physdev-is-bridged s-6-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-out vif18.3 --physdev-is-bridged s-6-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-out vif18.1 --physdev-is-bridged s-6-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-out vif18.0 --physdev-is-bridged s-6-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-out vif18.2 --physdev-is-bridged r-4-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-out vif19.1 --physdev-is-bridged r-4-VM all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-out vif19.0 --physdev-is-bridged i-2-7-def all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-out vif21.0 --physdev-is-bridged
經過如下規則能夠發現,安全組全部包都會經FORWARD BRIDGE-FIREWALL,而BRIDGE-FIREWALL的第一條是BRIDGE-DEFAULT-FIREWALL預先處理。默認DHCP的67,68端口容許。
Chain FORWARD (policy ACCEPT) target prot opt source destination BRIDGE-FIREWALL all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-is-bridged ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-out eth1 --physdev-is-bridged ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-out eth0 --physdev-is-bridged DROP all -- 0.0.0.0/0 0.0.0.0/0 Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain BRIDGE-DEFAULT-FIREWALL (1 references) target prot opt source destination ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED ACCEPT udp -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-is-bridged udp spt:68 dpt:67 ACCEPT udp -- 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-is-bridged udp spt:67 dpt:68
經過以上規則能夠發現,安全組對於組播包IGMP和除了DHCP廣播包,其餘都是是被丟棄的。
若是VM內部的應用須要組播,須要手動開啓刪除這個規則。
以上是VM默認規則,下篇將講解用戶自定義規則,涉及到Cloudstack 安全組的核心類 SecurityGroupManagerImpl.java和SecurityGroupManagerImpl2.java ,和一些相關API。