From bf64fa1bc89ccdbf2326d5ab41a050d19e2581d0 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Mon, 22 Dec 2014 03:43:12 +0100 Subject: [PATCH 01/14] feature #3175: Some refactor of SG classes --- src/vnm_mad/remotes/lib/address.rb | 78 ++++ src/vnm_mad/remotes/lib/command.rb | 87 ++++ src/vnm_mad/remotes/lib/security_groups.rb | 485 +++++++++++++++++++++ 3 files changed, 650 insertions(+) create mode 100644 src/vnm_mad/remotes/lib/address.rb create mode 100644 src/vnm_mad/remotes/lib/command.rb create mode 100644 src/vnm_mad/remotes/lib/security_groups.rb diff --git a/src/vnm_mad/remotes/lib/address.rb b/src/vnm_mad/remotes/lib/address.rb new file mode 100644 index 0000000000..dcb8f714f8 --- /dev/null +++ b/src/vnm_mad/remotes/lib/address.rb @@ -0,0 +1,78 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +module VNMMAD + +# The address module provides basic functions to manage IP addresses +module Address + + # This methods translates an address range to a set of IPv4 networks + # in CIDR notation + # @param ip_start [String] First IP of the range in dot notation + # @param size [String] The number of IPs in the range + # + # @return [Array] The networks in CIDR + def self.to_nets(ip_start, size) + nets = Array.new + ip_i = IPv4.to_i(ip_start) + + # Find the largest address block (look for the first 1-bit) + lblock = 0 + + lblock += 1 while (ip_i[lblock] == 0 && lblock < 32 ) + + # Allocate whole blocks till the size fits + while ( size >= 2**lblock ) + nets << "#{IPv4.to_s(ip_i)}/#{32-lblock}" + + ip_i += 2**lblock + size -= 2**lblock + + lblock += 1 while (ip_i[lblock] == 0 && lblock < 32 ) + end + + # Fit remaining address blocks + 32.downto(0) { |i| + next if size[i] == 0 + + nets << "#{IPv4.to_s(ip_i)}/#{32-i}" + + ip_i += 2**i + } + + return nets + end + + # This implementation module includes IPv4 management functions + # It MUST NOT be used in other VNMAD classes + module IPv4 + # Returns the binary equivalent of a IP address + # @param ip [String] IP in dot notation + # @return [Fixnum] IP as an integer + def self.to_i(ip) + ip.split(".").inject(0) {|t,e| (t << 8) + e.to_i } + end + + # Returns the string equivalent of a IP address + # @param ip [Fixnum] IP as an integer + # @return [String] IP in dot notation + def self.to_s(ip) + ip = 3.downto(0).collect {|s| (ip >> 8*s) & 0xff }.join('.') + end + end +end + +end diff --git a/src/vnm_mad/remotes/lib/command.rb b/src/vnm_mad/remotes/lib/command.rb new file mode 100644 index 0000000000..273c9354eb --- /dev/null +++ b/src/vnm_mad/remotes/lib/command.rb @@ -0,0 +1,87 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +module VNMMAD + +# This module includes functions to execute and manage Network commands. +module Command + + # Command configuration for common network commands. This CAN be adjust + # to local installations. Any modification requires to sync the hosts with + # onehost sync command. + COMMANDS = { + :ebtables => "sudo ebtables", + :iptables => "sudo iptables", + :brctl => "sudo brctl", + :ip => "sudo ip", + :virsh => "virsh -c qemu:///system", + :xm => Configuration::get_xen_command, + :ovs_vsctl=> "sudo ovs-vsctl", + :ovs_ofctl=> "sudo ovs-ofctl", + :lsmod => "lsmod", + :ipset => "sudo ipset" + } + + # Represents an Array of commands to be executed by the networking drivers + # The commands + class Commands < Array + + # Adds a new command to the command array + # @param cmd [String] the command, it can be a key defined in COMMANDS + # @para args[Array] Arguments for the command + def << (cmd, *args) + if COMMANDS.keys.include?(cmd.to_sym) + cmd_str = "#{COMMANDS[cmd.to_sym]} #{args.join(' ')}" + else + cmd_str = "#{cmd} #{args.join(' ')}" + end + + super cmd_str + end + + # Executes the commands array + # @return [String] the output of the commands + def run! + out = "" + + self.each{ |c| + out << `#{c}` + + raise StandardError, "Command Error: #{c}" if !$?.success? + } + + clear + + return out + end + end + + # This module include implementation specific functions. It MUST not be + # be used in other VNMAD classes. + module Configuration + # Return the command to talk to the Xen hypervisor xm or xl for + # Xen 3 and 4 + def self.get_xen_command + if system("ps axuww | grep -v grep | grep '\\bxen\\b'") + "sudo xm" + else + "sudo xl" + end + end + end +end + +end \ No newline at end of file diff --git a/src/vnm_mad/remotes/lib/security_groups.rb b/src/vnm_mad/remotes/lib/security_groups.rb new file mode 100644 index 0000000000..29ec6dec77 --- /dev/null +++ b/src/vnm_mad/remotes/lib/security_groups.rb @@ -0,0 +1,485 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +module VNMMAD + +# This module includes provides the abstractions to manage SecurityGroups +module SecurityGroup + + ############################################################################ + # Rule supports these (final and relevant) attributes: + # + # PROTOCOL (mandatory) + # - Specifies the protocol of the rule + # - values: ['ALL', 'TCP', 'UDP', 'ICMP', 'IPSEC'] + # + # RULE_TYPE (mandatory) + # - Specifies the direction of application of the rule + # - values: ['INBOUND', 'OUTBOUND'] + # + # RANGE (optional) + # - only works for protocols ['TCP', 'UDP'] + # - uses the iptables multiports syntax + # + # ICMP_TYPE (optional) + # - Only works for protocol 'ICMP' + # - Is either in the form of '' or '/', where both + # '' and '' are integers. This class has a helper method + # tgat expands '' into all the '/' subtypes. + # + # IP and SIZE (optional but must be specified together) + # - Can be applied to any protocol + # - IP is the first valid IP and SIZE is the number of consecutive IPs + ############################################################################ + class Rule + + # Rule type. + TYPES = { + :protocol, # Type 1: block the whole protocol + :portrange, # Type 2a: block a port range within a protocol + :icmp_type, # Type 2b: block selected icmp types + :net, # Type 3: block a whole protocol for a network + :net_portrange, # Type 4a: block a port range from a network + :net_icmp_type # Type 4b: block selected icmp types from a network + } + + attr_accessor :protocol, :rule_type, :range, :icmp_type, :ip, :size + attr_accessor :type + + # Initialize a new rule. + def initialize(rule) + @rule = rule + @protocol = @rule[:protocol].downcase.to_sym + @protocol = :esp if @protocol == :ipsec + + @rule_type = @rule[:rule_type].downcase.to_sym + @icmp_type = @rule[:icmp_type] + + @range = @rule[:range] + @ip = @rule[:ip] + @size = @rule[:size] + @type = set_type + end + + # Return the network blocks associated to the rule + # @return [Array] each network block in CIDR. + def net + return [] if @ip.nil? || @size.nil? + + Address::to_nets(@ip, @size) + end + + # Expand the ICMP type with associated codes if any + # @return [Array] expanded ICMP types to include all codes + def icmp_type_expand + if (codes = ICMP_TYPES_EXPANDED[@icmp_type.to_i]) + codes.collect{|e| "#{@icmp_type}/#{e}"} + else + ["#{@icmp_type}/0"] + end + end + + private: + # ICMP Codes for each ICMP type + ICMP_TYPES_EXPANDED = { + 3 => [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15], + 5 => [0, 1, 2, 3], + 11 => [0, 1], + 12 => [0, 1] + } + + # Depending on the combination of the rule attributes derive the + # rule type: + # + # ['PROTOCOL', 'RULE_TYPE'] => Type 1: 'protocol' + # ['PROTOCOL', 'RULE_TYPE', 'RANGE'] => Type 2A: 'portrange' + # ['PROTOCOL', 'RULE_TYPE', 'ICMP_TYPE'] => Type 2B: 'icmp_type' + # ['PROTOCOL', 'RULE_TYPE', 'IP', 'SIZE'] => Type 3: 'net' + # ['PROTOCOL', 'RULE_TYPE', 'IP', 'SIZE', 'RANGE'] => Type 4A: 'net_portrange' + # ['PROTOCOL', 'RULE_TYPE', 'IP', 'SIZE', 'ICMP_TYPE'] => Type 4B: 'net_icmp_type' + # + # @return [Symbol] The rule type + def set_type + if @ip.nil? && @size.nil? + return :icmp_type if !@icmp_type.nil? + return :portrange if !@range.nil? + return :protocol + end + else + return :net_icmp_type if !@icmp_type.nil? + return :net_portrange if !@range.nil? + return :net + end + end + end + + ############################################################################ + # A Rule implemented with the iptables/ipset Linux kernel facilities + ############################################################################ + class RuleIPTables < Rule + # Process the rule and generates the associated commands of the rule + # @param [Commands] cmd to add the rule commands + # @param [Hash] vars iptables attributes for the rule + def process(cmds, vars) + case @type + when :protocol + process_protocol(cmds, vars) + + when :portrange + process_portrange(cmds, vars) + + when :icmp_type + process_icmp_type(cmds, vars) + + when :net + process_net(cmds, vars) + + when :net_portrange + process_net_portrange(cmds, vars) + + when :net_icmp_type + process_net_icmp_type(cmds, vars) + end + end + end + + ######################################################################## + # Implementation of each rule type + ######################################################################## + private: + + # Implements the :protocol rule. Example: + # iptables -A one-3-0-i -p tcp -j RETURN + def process_protocol(cmds, vars) + chain = @rule_type == :inbound ? vars[:chain_in] : vars[:chain_out] + + cmds << :iptables "-A #{chain} -p #{@protocol} -j RETURN" + end + + # Implements the :portrange rule. Example: + # iptables -A one-3-0-o -p udp -m multiport --dports 80,22 -j RETURN + def process_portrange(cmds, vars) + chain = @rule_type == :inbound ? vars[:chain_in] : vars[:chain_out] + + cmds << :iptables "-A #{chain} -p #{@protocol} -m multiport" \ + " --dports #{@range} -j RETURN" + end + + # Implements the :icmp_type rule. Example: + # iptables -A one-3-0-o -p icmp --icmp-type 8 -j RETURN + def process_icmp_type(cmds, vars) + chain = @rule_type == :inbound ? vars[:chain_in] : vars[:chain_out] + + cmds << :iptables "-A #{chain} -p icmp --icmp-type #{@icmp_type}" \ + " -j RETURN" + end + + # Implements the :net rule. Example: + # ipset create one-3-0-1-i-tcp-n hash:net + # iptables -A one-3-0-i -p tcp -m set --match-set one-3-0-1-i src -j RETURN + # ipset add -exist one-3-0-1-i-tcp-n 10.0.0.0/24 + def process_net(cmds, vars) + if @rule_type == :inbound + chain = vars[:chain_in] + set = "#{vars[:set_sg_in]}-#{@protocol}-n" + dir = "src" + else + chain = : vars[:chain_out] + set = "#{vars[:set_sg_out]}-#{@protocol}-n" + dir = "dst" + end + + cmds << :ipset "create #{set} hash:net" + cmds << :iptables "-A #{chain} -p #{@protocol} -m set" \ + " --match-set #{set} #{dir} -j RETURN" + + net.each do |n| + cmds << :ipset "add -exist #{set} #{n}" + end + end + + # Implements the :net_portrange rule. Example: + # ipset create one-3-0-1-i-nr hash:net,port + # iptables -A one-3-0-i -m set --match-set one-3-0-1-i-nr src,dst -j RETURN + # ipset add -exist one-3-0-1-i-nr 10.0.0.0/24,tcp:80 + def process_net_portrange(cmds, vars) + if @rule_type == :inbound + chain = vars[:chain_in] + set = "#{vars[:set_sg_in]}-nr" + dir = "src,dst" + else + chain = : vars[:chain_out] + set = "#{vars[:set_sg_out]}-nr" + dir = "dst,dst" + end + + cmds << :ipset "create #{set} hash:net,port" + cmds << :iptables "-A #{chain} -m set --match-set" \ + "#{set} #{dir} -j RETURN" + + net.each do |n| + @range.split(",").each do |r| + r.gsub!(":","-") + net_range = "#{n},#{@protocol}:#{r}" + cmds << :ipset "add -exist #{set} #{net_range}" + end + end + end + + # Implements the :net_icmp_type rule. Example: + # ipset create one-3-0-1-i-ni hash:net,port + # iptables -A one-3-0-i -m set --match-set one-3-0-1-i-nr src,dst -j RETURN + # ipset add -exist one-3-0-1-i-ni 10.0.0.0/24,icmp:8/0 + def process_net_icmp_type(cmds, vars) + if rule.rule_type == :inbound + chain = vars[:chain_in] + set = "#{vars[:set_sg_in]}-ni" + dir = "src,dst" + else + chain = : vars[:chain_out] + set = "#{vars[:set_sg_out]}-ni" + dir = "dst,dst" + end + + cmds << :ipset "create #{set} hash:net,port" + cmds << :iptables "-A #{chain} -m set --match-set #{set} #{dir} -j RETURN" + + net.each do |n| + icmp_type_expand.each do |type_code| + cmds << :ipset "add -exist #{set} #{n},icmp:#{type_code}" + end if rule.icmp_type_expand + end + end + end + + ############################################################################ + # Base class for security groups. This class SHOULD NOT be used directly + ############################################################################ + class SecurityGroup + # Creates a new security group + # @param vm [VNMMAD::VM] a VM object + # @param nic [VNMMAD::NIC] the network interface + # @param sg_id [Fixnum] the security group ID + # @param rules [Array] to be applied to the NIC + def initialize(vm, nic, sg_id, rules) + @vm = vm + @nic = nic + @sg_id = sg_id + + @rules = [] + @invalid_rules = [] + + rules.each do |rule| + @rules << new_rule(rule) + end if rules + end + + # Default factory method for the SecurityGroup class. It MUST be + # overriden in derived classes + def new_rule(rule) + Rule.new(rule) + end + end + + ############################################################################ + # This class represents a SecurityGroup implemented with iptables/ipset + # Kernel facilities. + ############################################################################ + class SecurityGroupIPTables < SecurityGroup + + # All iptable rules will be added to this chain. + GLOBAL_CHAIN = "opennebula" + + # Creates a new security group + # @param vm [VNMMAD::VM] a VM object + # @param nic [VNMMAD::NIC] the network interface + # @param sg_id [Fixnum] the security group ID + # @param rules [Array] to be applied to the NIC + def initialize(vm, nic, sg_id, rules) + super + + @commands = Commands.new + + @vars = SecurityGroupIPTables.vars(@vm, @nic, @sg_id) + + @chain_in = vars[:chain_in] + @chain_out = vars[:chain_out] + @set_sg_in = vars[:set_sg_in] + @set_sg_out = vars[:set_sg_out] + end + + def process_rules + @rules.each do |rule| + rule.process(@commands, @vars) + end + + @commands.uniq! + end + + def run! + @commands.run! + end + + ############################################################################ + # Static methods + ############################################################################ + + def self.global_bootstrap + info = self.info + + if !info[:iptables_s].split("\n").include? "-N #{GLOBAL_CHAIN}" + commands = Commands.new + + commands.iptables "-N #{GLOBAL_CHAIN}" + commands.iptables "-A FORWARD -m physdev --physdev-is-bridged -j #{GLOBAL_CHAIN}" + commands.iptables "-A #{GLOBAL_CHAIN} -j ACCEPT" + + commands.run! + end + end + + def self.nic_pre(vm, nic) + commands = Commands.new + + vars = self.vars(vm, nic) + + chain = vars[:chain] + chain_in = vars[:chain_in] + chain_out = vars[:chain_out] + + # create chains + commands.iptables "-N #{chain_in}" # inbound + commands.iptables "-N #{chain_out}" # outbound + + # Send traffic to the NIC chains + commands.iptables"-I #{GLOBAL_CHAIN} -m physdev --physdev-out #{nic[:tap]} --physdev-is-bridged -j #{chain_in}" + commands.iptables"-I #{GLOBAL_CHAIN} -m physdev --physdev-in #{nic[:tap]} --physdev-is-bridged -j #{chain_out}" + + # Mac-spofing + if nic[:filter_mac_spoofing] == "YES" + commands.iptables"-A #{chain_out} -m mac ! --mac-source #{nic[:mac]} -j DROP" + end + + # IP-spofing + if nic[:filter_ip_spoofing] == "YES" + commands.iptables"-A #{chain_out} ! --source #{nic[:ip]} -j DROP" + end + + # Related, Established + commands.iptables"-A #{chain_in} -m state --state ESTABLISHED,RELATED -j ACCEPT" + commands.iptables"-A #{chain_out} -m state --state ESTABLISHED,RELATED -j ACCEPT" + + commands.run! + end + + def self.nic_post(vm, nic) + vars = self.vars(vm, nic) + chain_in = vars[:chain_in] + chain_out = vars[:chain_out] + + commands = Commands.new + commands.iptables("-A #{chain_in} -j DROP") + commands.iptables("-A #{chain_out} -j DROP") + + commands.run! + end + + def self.nic_deactivate(vm, nic) + vars = self.vars(vm, nic) + chain = vars[:chain] + chain_in = vars[:chain_in] + chain_out = vars[:chain_out] + + info = self.info + iptables_forwards = info[:iptables_forwards] + iptables_s = info[:iptables_s] + ipset_list = info[:ipset_list] + + commands = Commands.new + + iptables_forwards.lines.reverse_each do |line| + fields = line.split + if [chain_in, chain_out].include?(fields[1]) + n = fields[0] + commands.iptables("-D #{GLOBAL_CHAIN} #{n}") + end + end + + remove_chains = [] + iptables_s.lines.each do |line| + if line.match(/^-N #{chain}/) + remove_chains << line.split[1] + end + end + remove_chains.each {|c| commands.iptables("-F #{c}") } + remove_chains.each {|c| commands.iptables("-X #{c}") } + + ipset_list.lines.each do |line| + if line.match(/^#{chain}/) + set = line.strip + commands.ipset("destroy #{set}") + end + end + + commands.run! + end + + def self.info + commands = Commands.new + + commands.iptables("-S") + iptables_s = commands.run! + + if iptables_s.match(/^-N #{GLOBAL_CHAIN}$/) + commands.iptables("-L #{GLOBAL_CHAIN} --line-numbers") + iptables_forwards = commands.run! + else + iptables_forwards = "" + end + + commands.ipset("list -name") + ipset_list = commands.run! + + { + :iptables_forwards => iptables_forwards, + :iptables_s => iptables_s, + :ipset_list => ipset_list + } + end + + + def self.vars(vm, nic, sg_id = nil) + vm_id = vm['ID'] + nic_id = nic[:nic_id] + + vars = {} + + vars[:vm_id] = vm_id, + vars[:nic_id] = nic_id, + vars[:chain] = "one-#{vm_id}-#{nic_id}", + vars[:chain_in] = "#{vars[:chain]}-i", + vars[:chain_out] = "#{vars[:chain]}-o" + + if sg_id + vars[:set_sg_in] = "#{vars[:chain]}-#{sg_id}-i" + vars[:set_sg_out] = "#{vars[:chain]}-#{sg_id}-o" + end + + vars + end +end + +end \ No newline at end of file From 0e49cf00bbab5ee2584dd0a9b98c9297c8f24e51 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Mon, 22 Dec 2014 11:48:51 +0100 Subject: [PATCH 02/14] feature #3175: More refactor --- src/vnm_mad/remotes/lib/security_groups.rb | 409 ++++-------------- .../remotes/lib/security_groups_iptables.rb | 337 +++++++++++++++ 2 files changed, 411 insertions(+), 335 deletions(-) create mode 100644 src/vnm_mad/remotes/lib/security_groups_iptables.rb diff --git a/src/vnm_mad/remotes/lib/security_groups.rb b/src/vnm_mad/remotes/lib/security_groups.rb index 29ec6dec77..ca138ff0d6 100644 --- a/src/vnm_mad/remotes/lib/security_groups.rb +++ b/src/vnm_mad/remotes/lib/security_groups.rb @@ -16,8 +16,8 @@ module VNMMAD -# This module includes provides the abstractions to manage SecurityGroups -module SecurityGroup +# This module includes provides the abstractions to implement SecurityGroups +module SGBase ############################################################################ # Rule supports these (final and relevant) attributes: @@ -45,7 +45,6 @@ module SecurityGroup # - IP is the first valid IP and SIZE is the number of consecutive IPs ############################################################################ class Rule - # Rule type. TYPES = { :protocol, # Type 1: block the whole protocol @@ -56,9 +55,6 @@ module SecurityGroup :net_icmp_type # Type 4b: block selected icmp types from a network } - attr_accessor :protocol, :rule_type, :range, :icmp_type, :ip, :size - attr_accessor :type - # Initialize a new rule. def initialize(rule) @rule = rule @@ -74,64 +70,8 @@ module SecurityGroup @type = set_type end - # Return the network blocks associated to the rule - # @return [Array] each network block in CIDR. - def net - return [] if @ip.nil? || @size.nil? - - Address::to_nets(@ip, @size) - end - - # Expand the ICMP type with associated codes if any - # @return [Array] expanded ICMP types to include all codes - def icmp_type_expand - if (codes = ICMP_TYPES_EXPANDED[@icmp_type.to_i]) - codes.collect{|e| "#{@icmp_type}/#{e}"} - else - ["#{@icmp_type}/0"] - end - end - - private: - # ICMP Codes for each ICMP type - ICMP_TYPES_EXPANDED = { - 3 => [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15], - 5 => [0, 1, 2, 3], - 11 => [0, 1], - 12 => [0, 1] - } - - # Depending on the combination of the rule attributes derive the - # rule type: - # - # ['PROTOCOL', 'RULE_TYPE'] => Type 1: 'protocol' - # ['PROTOCOL', 'RULE_TYPE', 'RANGE'] => Type 2A: 'portrange' - # ['PROTOCOL', 'RULE_TYPE', 'ICMP_TYPE'] => Type 2B: 'icmp_type' - # ['PROTOCOL', 'RULE_TYPE', 'IP', 'SIZE'] => Type 3: 'net' - # ['PROTOCOL', 'RULE_TYPE', 'IP', 'SIZE', 'RANGE'] => Type 4A: 'net_portrange' - # ['PROTOCOL', 'RULE_TYPE', 'IP', 'SIZE', 'ICMP_TYPE'] => Type 4B: 'net_icmp_type' - # - # @return [Symbol] The rule type - def set_type - if @ip.nil? && @size.nil? - return :icmp_type if !@icmp_type.nil? - return :portrange if !@range.nil? - return :protocol - end - else - return :net_icmp_type if !@icmp_type.nil? - return :net_portrange if !@range.nil? - return :net - end - end - end - - ############################################################################ - # A Rule implemented with the iptables/ipset Linux kernel facilities - ############################################################################ - class RuleIPTables < Rule # Process the rule and generates the associated commands of the rule - # @param [Commands] cmd to add the rule commands + # @param [Commands] cmd to add the rule commands to # @param [Hash] vars iptables attributes for the rule def process(cmds, vars) case @type @@ -154,115 +94,83 @@ module SecurityGroup process_net_icmp_type(cmds, vars) end end + end + + # Return the network blocks associated to the rule + # @return [Array] each network block in CIDR. + def net + return [] if @ip.nil? || @size.nil? + + Address::to_nets(@ip, @size) end - ######################################################################## - # Implementation of each rule type - ######################################################################## - private: - - # Implements the :protocol rule. Example: - # iptables -A one-3-0-i -p tcp -j RETURN - def process_protocol(cmds, vars) - chain = @rule_type == :inbound ? vars[:chain_in] : vars[:chain_out] - - cmds << :iptables "-A #{chain} -p #{@protocol} -j RETURN" - end - - # Implements the :portrange rule. Example: - # iptables -A one-3-0-o -p udp -m multiport --dports 80,22 -j RETURN - def process_portrange(cmds, vars) - chain = @rule_type == :inbound ? vars[:chain_in] : vars[:chain_out] - - cmds << :iptables "-A #{chain} -p #{@protocol} -m multiport" \ - " --dports #{@range} -j RETURN" - end - - # Implements the :icmp_type rule. Example: - # iptables -A one-3-0-o -p icmp --icmp-type 8 -j RETURN - def process_icmp_type(cmds, vars) - chain = @rule_type == :inbound ? vars[:chain_in] : vars[:chain_out] - - cmds << :iptables "-A #{chain} -p icmp --icmp-type #{@icmp_type}" \ - " -j RETURN" - end - - # Implements the :net rule. Example: - # ipset create one-3-0-1-i-tcp-n hash:net - # iptables -A one-3-0-i -p tcp -m set --match-set one-3-0-1-i src -j RETURN - # ipset add -exist one-3-0-1-i-tcp-n 10.0.0.0/24 - def process_net(cmds, vars) - if @rule_type == :inbound - chain = vars[:chain_in] - set = "#{vars[:set_sg_in]}-#{@protocol}-n" - dir = "src" + # Expand the ICMP type with associated codes if any + # @return [Array] expanded ICMP types to include all codes + def icmp_type_expand + if (codes = ICMP_TYPES_EXPANDED[@icmp_type.to_i]) + codes.collect{|e| "#{@icmp_type}/#{e}"} else - chain = : vars[:chain_out] - set = "#{vars[:set_sg_out]}-#{@protocol}-n" - dir = "dst" - end - - cmds << :ipset "create #{set} hash:net" - cmds << :iptables "-A #{chain} -p #{@protocol} -m set" \ - " --match-set #{set} #{dir} -j RETURN" - - net.each do |n| - cmds << :ipset "add -exist #{set} #{n}" + ["#{@icmp_type}/0"] end end - # Implements the :net_portrange rule. Example: - # ipset create one-3-0-1-i-nr hash:net,port - # iptables -A one-3-0-i -m set --match-set one-3-0-1-i-nr src,dst -j RETURN - # ipset add -exist one-3-0-1-i-nr 10.0.0.0/24,tcp:80 - def process_net_portrange(cmds, vars) - if @rule_type == :inbound - chain = vars[:chain_in] - set = "#{vars[:set_sg_in]}-nr" - dir = "src,dst" - else - chain = : vars[:chain_out] - set = "#{vars[:set_sg_out]}-nr" - dir = "dst,dst" - end + private: - cmds << :ipset "create #{set} hash:net,port" - cmds << :iptables "-A #{chain} -m set --match-set" \ - "#{set} #{dir} -j RETURN" + # ICMP Codes for each ICMP type + ICMP_TYPES_EXPANDED = { + 3 => [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15], + 5 => [0, 1, 2, 3], + 11 => [0, 1], + 12 => [0, 1] + } - net.each do |n| - @range.split(",").each do |r| - r.gsub!(":","-") - net_range = "#{n},#{@protocol}:#{r}" - cmds << :ipset "add -exist #{set} #{net_range}" + # Depending on the combination of the rule attributes derive the + # rule type: + # + # @protocol + @rule_type => Type 1: 'protocol' + # @protocol + @rule_type + @range => Type 2A: 'portrange' + # @protocol + @rule_type + @icmp_type => Type 2B: 'icmp_type' + # @protocol + @rule_type + @ip + @size => Type 3: 'net' + # @protocol + @rule_type + @ip + @size + @range => Type 4A: 'net_portrange' + # @protocol + @rule_type + @ip + @size + @icmp_type => Type 4B: 'net_icmp_type' + # + # @return [Symbol] The rule type + def set_type + if @ip.nil? && @size.nil? + return :icmp_type if !@icmp_type.nil? + return :portrange if !@range.nil? + return :protocol end - end - end - - # Implements the :net_icmp_type rule. Example: - # ipset create one-3-0-1-i-ni hash:net,port - # iptables -A one-3-0-i -m set --match-set one-3-0-1-i-nr src,dst -j RETURN - # ipset add -exist one-3-0-1-i-ni 10.0.0.0/24,icmp:8/0 - def process_net_icmp_type(cmds, vars) - if rule.rule_type == :inbound - chain = vars[:chain_in] - set = "#{vars[:set_sg_in]}-ni" - dir = "src,dst" else - chain = : vars[:chain_out] - set = "#{vars[:set_sg_out]}-ni" - dir = "dst,dst" - end - - cmds << :ipset "create #{set} hash:net,port" - cmds << :iptables "-A #{chain} -m set --match-set #{set} #{dir} -j RETURN" - - net.each do |n| - icmp_type_expand.each do |type_code| - cmds << :ipset "add -exist #{set} #{n},icmp:#{type_code}" - end if rule.icmp_type_expand + return :net_icmp_type if !@icmp_type.nil? + return :net_portrange if !@range.nil? + return :net end end + + ######################################################################## + # Dummy process methods for each rule type. These MUST be overriden + # in derived classes + # @param cmds [Commands] commands to implement the rule + # @param vars [Hash] with specific rule implementation variables + ######################################################################## + def process_protocol(cmds, vars) + end + + def process_portrange(cmds, vars) + end + + def process_icmp_type(cmds, vars) + end + + def process_net(cmds, vars) + end + + def process_net_portrange(cmds, vars) + end + + def process_net_icmp_type(cmds, vars) + end end ############################################################################ @@ -280,7 +188,9 @@ module SecurityGroup @sg_id = sg_id @rules = [] - @invalid_rules = [] + @vars = {} + + @commands = Commands.new rules.each do |rule| @rules << new_rule(rule) @@ -292,35 +202,8 @@ module SecurityGroup def new_rule(rule) Rule.new(rule) end - end - - ############################################################################ - # This class represents a SecurityGroup implemented with iptables/ipset - # Kernel facilities. - ############################################################################ - class SecurityGroupIPTables < SecurityGroup - - # All iptable rules will be added to this chain. - GLOBAL_CHAIN = "opennebula" - - # Creates a new security group - # @param vm [VNMMAD::VM] a VM object - # @param nic [VNMMAD::NIC] the network interface - # @param sg_id [Fixnum] the security group ID - # @param rules [Array] to be applied to the NIC - def initialize(vm, nic, sg_id, rules) - super - - @commands = Commands.new - - @vars = SecurityGroupIPTables.vars(@vm, @nic, @sg_id) - - @chain_in = vars[:chain_in] - @chain_out = vars[:chain_out] - @set_sg_in = vars[:set_sg_in] - @set_sg_out = vars[:set_sg_out] - end + # Generates the iptables/ipset commands to implement this security group def process_rules @rules.each do |rule| rule.process(@commands, @vars) @@ -329,157 +212,13 @@ module SecurityGroup @commands.uniq! end + # Execute the implementation commands, process_rules MUST be called + # before this method def run! @commands.run! end - - ############################################################################ - # Static methods - ############################################################################ - - def self.global_bootstrap - info = self.info - - if !info[:iptables_s].split("\n").include? "-N #{GLOBAL_CHAIN}" - commands = Commands.new - - commands.iptables "-N #{GLOBAL_CHAIN}" - commands.iptables "-A FORWARD -m physdev --physdev-is-bridged -j #{GLOBAL_CHAIN}" - commands.iptables "-A #{GLOBAL_CHAIN} -j ACCEPT" - - commands.run! - end end - def self.nic_pre(vm, nic) - commands = Commands.new - - vars = self.vars(vm, nic) - - chain = vars[:chain] - chain_in = vars[:chain_in] - chain_out = vars[:chain_out] - - # create chains - commands.iptables "-N #{chain_in}" # inbound - commands.iptables "-N #{chain_out}" # outbound - - # Send traffic to the NIC chains - commands.iptables"-I #{GLOBAL_CHAIN} -m physdev --physdev-out #{nic[:tap]} --physdev-is-bridged -j #{chain_in}" - commands.iptables"-I #{GLOBAL_CHAIN} -m physdev --physdev-in #{nic[:tap]} --physdev-is-bridged -j #{chain_out}" - - # Mac-spofing - if nic[:filter_mac_spoofing] == "YES" - commands.iptables"-A #{chain_out} -m mac ! --mac-source #{nic[:mac]} -j DROP" - end - - # IP-spofing - if nic[:filter_ip_spoofing] == "YES" - commands.iptables"-A #{chain_out} ! --source #{nic[:ip]} -j DROP" - end - - # Related, Established - commands.iptables"-A #{chain_in} -m state --state ESTABLISHED,RELATED -j ACCEPT" - commands.iptables"-A #{chain_out} -m state --state ESTABLISHED,RELATED -j ACCEPT" - - commands.run! - end - - def self.nic_post(vm, nic) - vars = self.vars(vm, nic) - chain_in = vars[:chain_in] - chain_out = vars[:chain_out] - - commands = Commands.new - commands.iptables("-A #{chain_in} -j DROP") - commands.iptables("-A #{chain_out} -j DROP") - - commands.run! - end - - def self.nic_deactivate(vm, nic) - vars = self.vars(vm, nic) - chain = vars[:chain] - chain_in = vars[:chain_in] - chain_out = vars[:chain_out] - - info = self.info - iptables_forwards = info[:iptables_forwards] - iptables_s = info[:iptables_s] - ipset_list = info[:ipset_list] - - commands = Commands.new - - iptables_forwards.lines.reverse_each do |line| - fields = line.split - if [chain_in, chain_out].include?(fields[1]) - n = fields[0] - commands.iptables("-D #{GLOBAL_CHAIN} #{n}") - end - end - - remove_chains = [] - iptables_s.lines.each do |line| - if line.match(/^-N #{chain}/) - remove_chains << line.split[1] - end - end - remove_chains.each {|c| commands.iptables("-F #{c}") } - remove_chains.each {|c| commands.iptables("-X #{c}") } - - ipset_list.lines.each do |line| - if line.match(/^#{chain}/) - set = line.strip - commands.ipset("destroy #{set}") - end - end - - commands.run! - end - - def self.info - commands = Commands.new - - commands.iptables("-S") - iptables_s = commands.run! - - if iptables_s.match(/^-N #{GLOBAL_CHAIN}$/) - commands.iptables("-L #{GLOBAL_CHAIN} --line-numbers") - iptables_forwards = commands.run! - else - iptables_forwards = "" - end - - commands.ipset("list -name") - ipset_list = commands.run! - - { - :iptables_forwards => iptables_forwards, - :iptables_s => iptables_s, - :ipset_list => ipset_list - } - end - - - def self.vars(vm, nic, sg_id = nil) - vm_id = vm['ID'] - nic_id = nic[:nic_id] - - vars = {} - - vars[:vm_id] = vm_id, - vars[:nic_id] = nic_id, - vars[:chain] = "one-#{vm_id}-#{nic_id}", - vars[:chain_in] = "#{vars[:chain]}-i", - vars[:chain_out] = "#{vars[:chain]}-o" - - if sg_id - vars[:set_sg_in] = "#{vars[:chain]}-#{sg_id}-i" - vars[:set_sg_out] = "#{vars[:chain]}-#{sg_id}-o" - end - - vars - end end end \ No newline at end of file diff --git a/src/vnm_mad/remotes/lib/security_groups_iptables.rb b/src/vnm_mad/remotes/lib/security_groups_iptables.rb new file mode 100644 index 0000000000..4c57ad96d1 --- /dev/null +++ b/src/vnm_mad/remotes/lib/security_groups_iptables.rb @@ -0,0 +1,337 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +module VNMMAD + +# This module implements the SecurityGroup abstraction on top of iptables +module SGIPTables + + ############################################################################ + # A Rule implemented with the iptables/ipset Linux kernel facilities + ############################################################################ + class RuleIPTables < Rule + ######################################################################## + # Implementation of each rule type + ######################################################################## + private: + + # Implements the :protocol rule. Example: + # iptables -A one-3-0-i -p tcp -j RETURN + def process_protocol(cmds, vars) + chain = @rule_type == :inbound ? vars[:chain_in] : vars[:chain_out] + + cmds << :iptables "-A #{chain} -p #{@protocol} -j RETURN" + end + + # Implements the :portrange rule. Example: + # iptables -A one-3-0-o -p udp -m multiport --dports 80,22 -j RETURN + def process_portrange(cmds, vars) + chain = @rule_type == :inbound ? vars[:chain_in] : vars[:chain_out] + + cmds << :iptables "-A #{chain} -p #{@protocol} -m multiport" \ + " --dports #{@range} -j RETURN" + end + + # Implements the :icmp_type rule. Example: + # iptables -A one-3-0-o -p icmp --icmp-type 8 -j RETURN + def process_icmp_type(cmds, vars) + chain = @rule_type == :inbound ? vars[:chain_in] : vars[:chain_out] + + cmds << :iptables "-A #{chain} -p icmp --icmp-type #{@icmp_type}" \ + " -j RETURN" + end + + # Implements the :net rule. Example: + # ipset create one-3-0-1-i-tcp-n hash:net + # iptables -A one-3-0-i -p tcp -m set --match-set one-3-0-1-i src -j RETURN + # ipset add -exist one-3-0-1-i-tcp-n 10.0.0.0/24 + def process_net(cmds, vars) + if @rule_type == :inbound + chain = vars[:chain_in] + set = "#{vars[:set_sg_in]}-#{@protocol}-n" + dir = "src" + else + chain = : vars[:chain_out] + set = "#{vars[:set_sg_out]}-#{@protocol}-n" + dir = "dst" + end + + cmds << :ipset "create #{set} hash:net" + cmds << :iptables "-A #{chain} -p #{@protocol} -m set" \ + " --match-set #{set} #{dir} -j RETURN" + + net.each do |n| + cmds << :ipset "add -exist #{set} #{n}" + end + end + + # Implements the :net_portrange rule. Example: + # ipset create one-3-0-1-i-nr hash:net,port + # iptables -A one-3-0-i -m set --match-set one-3-0-1-i-nr src,dst -j RETURN + # ipset add -exist one-3-0-1-i-nr 10.0.0.0/24,tcp:80 + def process_net_portrange(cmds, vars) + if @rule_type == :inbound + chain = vars[:chain_in] + set = "#{vars[:set_sg_in]}-nr" + dir = "src,dst" + else + chain = : vars[:chain_out] + set = "#{vars[:set_sg_out]}-nr" + dir = "dst,dst" + end + + cmds << :ipset "create #{set} hash:net,port" + cmds << :iptables "-A #{chain} -m set --match-set" \ + "#{set} #{dir} -j RETURN" + + net.each do |n| + @range.split(",").each do |r| + r.gsub!(":","-") + net_range = "#{n},#{@protocol}:#{r}" + cmds << :ipset "add -exist #{set} #{net_range}" + end + end + end + + # Implements the :net_icmp_type rule. Example: + # ipset create one-3-0-1-i-ni hash:net,port + # iptables -A one-3-0-i -m set --match-set one-3-0-1-i-nr src,dst -j RETURN + # ipset add -exist one-3-0-1-i-ni 10.0.0.0/24,icmp:8/0 + def process_net_icmp_type(cmds, vars) + if rule.rule_type == :inbound + chain = vars[:chain_in] + set = "#{vars[:set_sg_in]}-ni" + dir = "src,dst" + else + chain = : vars[:chain_out] + set = "#{vars[:set_sg_out]}-ni" + dir = "dst,dst" + end + + cmds << :ipset "create #{set} hash:net,port" + cmds << :iptables "-A #{chain} -m set --match-set #{set} #{dir} -j RETURN" + + net.each do |n| + icmp_type_expand.each do |type_code| + cmds << :ipset "add -exist #{set} #{n},icmp:#{type_code}" + end if rule.icmp_type_expand + end + end + end + + ############################################################################ + # This class represents a SecurityGroup implemented with iptables/ipset + # Kernel facilities. + ############################################################################ + class SecurityGroupIPTables < SecurityGroup + def initialize(vm, nic, sg_id, rules) + super + + @vars = SGIPTables.vars(@vm, @nic, @sg_id) + end + end + + ############################################################################ + # Methods to configure the hypervisor iptables rules. All the rules are + # added to the GLOBAL_CHAIN chain. By default this chain is "opennebula" + ############################################################################ + + GLOBAL_CHAIN = "opennebula" + + # Get information from the current iptables rules and chains + # @return [Hash] with the following keys: + # - :iptables_forwards + # - :iptables_s + # - :ipset_list + def self.info + commands = Commands.new + + commands << :iptables "-S" + iptables_s = commands.run! + + iptables_forwards = "" + + if iptables_s.match(/^-N #{GLOBAL_CHAIN}$/) + commands.iptables("-L #{GLOBAL_CHAIN} --line-numbers") + iptables_forwards = commands.run! + end + + commands << :ipset "list -name" + ipset_list = commands.run! + + { + :iptables_forwards => iptables_forwards, + :iptables_s => iptables_s, + :ipset_list => ipset_list + } + end + + # Bootstrap the OpenNebula chains and rules. This method: + # 1.- Creates the GLOBAL_CHAIN chain + # 2.- Forwards the bridge traffic to the GLOBAL_CHAIN + # 3.- By default ACCEPT all traffic + def self.global_bootstrap + info = SGIPTables.info + + return if info[:iptables_s].split("\n").include? "-N #{GLOBAL_CHAIN}" + + commands = Commands.new + + commands.iptables "-N #{GLOBAL_CHAIN}" + commands.iptables "-A FORWARD -m physdev --physdev-is-bridged -j #{GLOBAL_CHAIN}" + commands.iptables "-A #{GLOBAL_CHAIN} -j ACCEPT" + + commands.run! + end + + # Returns the base chain and ipset names for the VM + # @param vm [VM] the virtual machine + # @param nic [Nic] of the VM + # @param sg_id [Fixnum] ID of the SecurityGroup if any + # + # @return [Hash] with the :chain, :chain_in, :chain_out chain names, and + # :set_sg_in and :set_seg_out ipset names. + def self.vars(vm, nic, sg_id = nil) + vm_id = vm['ID'] + nic_id = nic[:nic_id] + + vars = {} + + vars[:vm_id] = vm_id, + vars[:nic_id] = nic_id, + vars[:chain] = "one-#{vm_id}-#{nic_id}", + vars[:chain_in] = "#{vars[:chain]}-i", + vars[:chain_out] = "#{vars[:chain]}-o" + + if sg_id + vars[:set_sg_in] = "#{vars[:chain]}-#{sg_id}-i" + vars[:set_sg_out] = "#{vars[:chain]}-#{sg_id}-o" + end + + vars + end + + # Bootstrap NIC rules for the interface. It creates the :chain_in and + # :chain_out and sets up FORWARD rules to these chains for inbound and + # outbound traffic. + # + # This method also sets mac_spoofing, and ip_spoofing rules + # + # Example, for VM 3 and NIC 0 + # iptables -N one-3-0-i + # iptables -N one-3-0-o + # iptables -I opennebula -m physdev --physdev-out vnet0 --physdev-is-bridged -j one-3-0-i" + # iptables -I opennebula -m physdev --physdev-in vnet0 --physdev-is-bridged -j one-3-0-o" + # iptables -A one-3-0-i -m state --state ESTABLISHED,RELATED -j ACCEPT + # iptables -A one-3-0-o -m state --state ESTABLISHED,RELATED -j ACCEPT + # + # Mac spoofing (no output traffic from a different MAC) + # iptables -A one-3-0-o -m mac ! --mac-source 02:00:00:00:00:01 -j DROP + # + # IP spoofing + # iptables -A one-3-0-o ! --source 10.0.0.1 -j DROP + def self.nic_pre(vm, nic) + commands = Commands.new + + vars = SGIPTables.vars(vm, nic) + + chain = vars[:chain] + chain_in = vars[:chain_in] + chain_out = vars[:chain_out] + + # create chains + commands << :iptables "-N #{chain_in}" # inbound + commands << :iptables "-N #{chain_out}" # outbound + + # Send traffic to the NIC chains + commands << :iptables "-I #{GLOBAL_CHAIN} -m physdev --physdev-out #{nic[:tap]} --physdev-is-bridged -j #{chain_in}" + commands << :iptables "-I #{GLOBAL_CHAIN} -m physdev --physdev-in #{nic[:tap]} --physdev-is-bridged -j #{chain_out}" + + # Mac-spofing + if nic[:filter_mac_spoofing] == "YES" + commands << :iptables "-A #{chain_out} -m mac ! --mac-source #{nic[:mac]} -j DROP" + end + + # IP-spofing + if nic[:filter_ip_spoofing] == "YES" + commands << :iptables "-A #{chain_out} ! --source #{nic[:ip]} -j DROP" + end + + # Related, Established + commands << :iptables "-A #{chain_in} -m state --state ESTABLISHED,RELATED -j ACCEPT" + commands << :iptables "-A #{chain_out} -m state --state ESTABLISHED,RELATED -j ACCEPT" + + commands.run! + end + + # Sets the default policy to DROP for the NIC rules. Example + # iptables -A one-3-0-i -j DROP + # iptables -A one-3-0-o -j DROP + def self.nic_post(vm, nic) + vars = SGIPTables.vars(vm, nic) + chain_in = vars[:chain_in] + chain_out = vars[:chain_out] + + commands = Commands.new + commands << :iptables "-A #{chain_in} -j DROP" + commands << :iptables "-A #{chain_out} -j DROP" + + commands.run! + end + + # Removes all the rules associated to a VM and NIC + def self.nic_deactivate(vm, nic) + vars = SGIPTables.vars(vm, nic) + chain = vars[:chain] + chain_in = vars[:chain_in] + chain_out = vars[:chain_out] + + info = self.info + iptables_forwards = info[:iptables_forwards] + iptables_s = info[:iptables_s] + ipset_list = info[:ipset_list] + + commands = Commands.new + + iptables_forwards.lines.reverse_each do |line| + fields = line.split + if [chain_in, chain_out].include?(fields[1]) + n = fields[0] + commands << :iptables "-D #{GLOBAL_CHAIN} #{n}" + end + end + + remove_chains = [] + iptables_s.lines.each do |line| + if line.match(/^-N #{chain}/) + remove_chains << line.split[1] + end + end + remove_chains.each {|c| commands << :iptables "-F #{c}" } + remove_chains.each {|c| commands << :iptables "-X #{c}" } + + ipset_list.lines.each do |line| + if line.match(/^#{chain}/) + set = line.strip + commands << :ipset "destroy #{set}" + end + end + + commands.run! + end +end + +end \ No newline at end of file From fba4deec6e1c67a7025b4a2f0ab1fc8943868152 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Mon, 22 Dec 2014 14:12:16 +0100 Subject: [PATCH 03/14] feature #3175: Moved Nic and VM to new modules --- src/vnm_mad/remotes/lib/address.rb | 3 +- src/vnm_mad/remotes/lib/command.rb | 3 +- src/vnm_mad/remotes/lib/nic.rb | 154 +++++++++++++++ src/vnm_mad/remotes/lib/opennebula_network.rb | 179 ++++++++++++++++++ src/vnm_mad/remotes/lib/security_groups.rb | 6 +- .../remotes/lib/security_groups_iptables.rb | 14 +- src/vnm_mad/remotes/lib/vm.rb | 110 +++++++++++ 7 files changed, 455 insertions(+), 14 deletions(-) create mode 100644 src/vnm_mad/remotes/lib/nic.rb create mode 100644 src/vnm_mad/remotes/lib/opennebula_network.rb create mode 100644 src/vnm_mad/remotes/lib/vm.rb diff --git a/src/vnm_mad/remotes/lib/address.rb b/src/vnm_mad/remotes/lib/address.rb index dcb8f714f8..f32838b7d4 100644 --- a/src/vnm_mad/remotes/lib/address.rb +++ b/src/vnm_mad/remotes/lib/address.rb @@ -16,8 +16,7 @@ module VNMMAD -# The address module provides basic functions to manage IP addresses -module Address +module VNMNetwork # This methods translates an address range to a set of IPv4 networks # in CIDR notation diff --git a/src/vnm_mad/remotes/lib/command.rb b/src/vnm_mad/remotes/lib/command.rb index 273c9354eb..3242283d29 100644 --- a/src/vnm_mad/remotes/lib/command.rb +++ b/src/vnm_mad/remotes/lib/command.rb @@ -16,8 +16,7 @@ module VNMMAD -# This module includes functions to execute and manage Network commands. -module Command +module VNMNetwork # Command configuration for common network commands. This CAN be adjust # to local installations. Any modification requires to sync the hosts with diff --git a/src/vnm_mad/remotes/lib/nic.rb b/src/vnm_mad/remotes/lib/nic.rb new file mode 100644 index 0000000000..9c85841e7d --- /dev/null +++ b/src/vnm_mad/remotes/lib/nic.rb @@ -0,0 +1,154 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +module VNMMAD + +module VNMNetwork + + # This class represents the NICS of a VM, it provides a factory method + # to create VMs of the given hyprtvisor + class Nics < Array + def initialize(hypervisor) + case hypervisor + when "kvm" + @nicClass = NicKVM + when "xen" + @nicClass = NicXen + when "vmware" + @nicClass = NicVMware + end + end + + def new_nic + @nicClass.new + end + end + + ############################################################################ + # Hypervisor specific implementation of network interfaces. Each class + # implements the following interface: + # - get_info to populste the VM.vm_info Hash + # - get_tap to set the [:tap] attribute with the associated NIC + ############################################################################ + + # A NIC using KVM. This class implements functions to get the physical + # interface that the NIC is using, based on the MAC address + class NicKVM < Hash + def initialize + super(nil) + end + + # Get the VM information with virsh dumpxml + def get_info(vm) + if vm.deploy_id + deploy_id = vm.deploy_id + else + deploy_id = vm['DEPLOY_ID'] + end + + if deploy_id and vm.vm_info[:dumpxml].nil? + vm.vm_info[:dumpxml] = `#{VNMNetwork::COMMANDS[:virsh]} dumpxml #{deploy_id} 2>/dev/null` + + vm.vm_info.each_key do |k| + vm.vm_info[k] = nil if vm.vm_info[k].to_s.strip.empty? + end + end + end + + # Look for the tap in + # devices/interface[@type='bridge']/mac[@address='']/../target" + def get_tap(vm) + dumpxml = vm.vm_info[:dumpxml] + + if dumpxml + dumpxml_root = REXML::Document.new(dumpxml).root + + xpath = "devices/interface[@type='bridge']/" \ + "mac[@address='#{self[:mac]}']/../target" + + tap = dumpxml_root.elements[xpath] + + self[:tap] = tap.attributes['dev'] if tap + end + + self + end + end + + + # A NIC using Xen. This class implements functions to get the physical interface + # that the NIC is using + class NicXen < Hash + def initialize + super(nil) + end + + def get_info(vm) + if vm.deploy_id + deploy_id = vm.deploy_id + else + deploy_id = vm['DEPLOY_ID'] + end + + if deploy_id and (vm.vm_info[:domid].nil? or vm.vm_info[:networks].nil?) + vm.vm_info[:domid] =`#{VNMNetwork::COMMANDS[:xm]} domid #{deploy_id}`.strip + vm.vm_info[:networks] =`#{VNMNetwork::COMMANDS[:xm]} network-list #{deploy_id}` + + vm.vm_info.each_key do |k| + vm.vm_info[k] = nil if vm.vm_info[k].to_s.strip.empty? + end + end + end + + def get_tap(vm) + domid = vm.vm_info[:domid] + + if domid + networks = vm.vm_info[:networks].split("\n")[1..-1] + networks.each do |net| + n = net.split + + iface_id = n[0] + iface_mac = n[2] + + if iface_mac == self[:mac] + self[:tap] = "vif#{domid}.#{iface_id}" + break + end + end + end + self + end + end + + # A NIC using VMware. This class implements functions to get the physical interface + # that the NIC is using + class NicVMware < Hash + def initialize + super(nil) + end + + def get_info(vm) + end + + def get_tap(vm) + self + end + end + +end + +end \ No newline at end of file diff --git a/src/vnm_mad/remotes/lib/opennebula_network.rb b/src/vnm_mad/remotes/lib/opennebula_network.rb new file mode 100644 index 0000000000..350d9adfe8 --- /dev/null +++ b/src/vnm_mad/remotes/lib/opennebula_network.rb @@ -0,0 +1,179 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +$: << File.dirname(__FILE__) +$: << File.join(File.dirname(__FILE__), '..') + +require 'rexml/document' +require 'base64' +require 'yaml' + +require 'one_firewall' +require 'one_sg' +require 'lib/vm' +require 'lib/nic' +require 'lib/address' +require 'lib/security_groups' +require 'lib/security_groups_iptables' + +require 'scripts_common' + +include OpenNebula + +begin + CONF = YAML.load_file( + File.join(File.dirname(__FILE__), "OpenNebulaNetwork.conf") + ) +rescue + CONF = { + :start_vlan => 2 + } +end + +# Set PATH +ENV['PATH'] = "#{ENV['PATH']}:/bin:/sbin:/usr/bin" + + +################################################################################ +# The VNMMAD module provides the basic abstraction to implement custom +# virtual network drivers. The VNMAD module includes: +# - VNMNetwork with base classes and main functionality +# - SGIPTables a module with a SG implementation based in iptables/ipset +################################################################################ +module VNMMAD + + ############################################################################ + # Base driver class to implement a Network driver. It relays on two filter + # drivers OpenNebulaFirewall and OpenNebulaSG. + ############################################################################ + class OpenNebulaNetwork + attr_reader :hypervisor, :vm + + # Creates new OpenNebulaNetwork using: + # @param vm_tpl [String] XML String from oned + # @param xpath_filter [String] to get relevant NICs for the driver + # @param deploy_id [String] + # @param hypervisor [String] + def initialize(vm_tpl, xpath_filter, deploy_id = nil, hypervisor = nil) + @locking = false + + if !hypervisor + @hypervisor = detect_hypervisor + else + @hypervisor = hypervisor + end + + @vm = VM.new(REXML::Document.new(vm_tpl).root, xpath_filter, + deploy_id, @hypervisor) + end + + # Creates a new OpenNebulaNetwork using: + # @param vm_64 [String] Base64 encoded XML String from oned + # @param deploy_id [String] + # @param hypervisor [String] + def self.from_base64(vm_64, deploy_id = nil, hypervisor = nil) + vm_xml = Base64::decode64(vm_64) + self.new(vm_xml, deploy_id, hypervisor) + end + + # Locking function to serialized driver operations if needed. Similar + # to flock. File is created as /tmp/onevnm--lock + def lock + if @locking + driver_name = self.class.name.downcase + @locking_file = File.open("/tmp/onevnm-#{driver_name}-lock","w") + @locking_file.flock(File::LOCK_EX) + end + end + + # Unlock driver execution mutex + def unlock + if @locking + @locking_file.close + end + end + + # Executes the given block on each NIC + def process(&block) + @vm.each_nic(block) + end + + # Return a string for the hypervisor + # @return [String] "kvm", "xen" or nil + def detect_hypervisor + lsmod = `#{VNMNetwork::COMMANDS[:lsmod]}` + xen_file = "/proc/xen/capabilities" + + if File.exists?(xen_file) + "xen" + elsif lsmod.match(/kvm/) + "kvm" + else + nil + end + end + + # Get hypervisor bridges + # @return [Hash] with the bridge names + def get_interfaces + bridges = Hash.new + brctl_exit =`#{VNMNetwork::COMMANDS[:brctl]} show` + + cur_bridge = "" + + brctl_exit.split("\n")[1..-1].each do |l| + l = l.split + + if l.length > 1 + cur_bridge = l[0] + + bridges[cur_bridge] = Array.new + bridges[cur_bridge] << l[3] if l[3] + else + bridges[cur_bridge] << l[0] + end + end + + bridges + end + + # Returns true if the template contains the deprecated firewall attributes: + # - ICMP + # - WHITE_PORTS_TCP + # - WHITE_PORTS_UDP + # - BLACK_PORTS_TCP + # - BLACK_PORTS_UDP + # + # @return Boolean + def self.has_fw_attrs?(vm_xml) + vm_root = REXML::Document.new(vm_xml).root + !vm_root.elements[OpenNebulaFirewall::XPATH_FILTER].nil? + end + + # Returns a filter object based on the contents of the template + # + # @return OpenNebulaFirewall or OpenNebulaSG object + def self.filter_driver(vm_64, deploy_id = nil, hypervisor = nil) + vm_xml = Base64::decode64(vm_64) + + if self.has_fw_attrs?(vm_xml) + OpenNebulaFirewall.new(vm_xml, deploy_id, hypervisor) + else + OpenNebulaSG.new(vm_xml, deploy_id, hypervisor) + end + end + end +end diff --git a/src/vnm_mad/remotes/lib/security_groups.rb b/src/vnm_mad/remotes/lib/security_groups.rb index ca138ff0d6..34a82f0cd5 100644 --- a/src/vnm_mad/remotes/lib/security_groups.rb +++ b/src/vnm_mad/remotes/lib/security_groups.rb @@ -17,7 +17,7 @@ module VNMMAD # This module includes provides the abstractions to implement SecurityGroups -module SGBase +module VNMNetwork ############################################################################ # Rule supports these (final and relevant) attributes: @@ -101,7 +101,7 @@ module SGBase def net return [] if @ip.nil? || @size.nil? - Address::to_nets(@ip, @size) + VNMNetwork::to_nets(@ip, @size) end # Expand the ICMP type with associated codes if any @@ -190,7 +190,7 @@ module SGBase @rules = [] @vars = {} - @commands = Commands.new + @commands = VNMNetwork::Commands.new rules.each do |rule| @rules << new_rule(rule) diff --git a/src/vnm_mad/remotes/lib/security_groups_iptables.rb b/src/vnm_mad/remotes/lib/security_groups_iptables.rb index 4c57ad96d1..86ec08d0b2 100644 --- a/src/vnm_mad/remotes/lib/security_groups_iptables.rb +++ b/src/vnm_mad/remotes/lib/security_groups_iptables.rb @@ -22,7 +22,7 @@ module SGIPTables ############################################################################ # A Rule implemented with the iptables/ipset Linux kernel facilities ############################################################################ - class RuleIPTables < Rule + class RuleIPTables < VNMNetwork::Rule ######################################################################## # Implementation of each rule type ######################################################################## @@ -136,7 +136,7 @@ module SGIPTables # This class represents a SecurityGroup implemented with iptables/ipset # Kernel facilities. ############################################################################ - class SecurityGroupIPTables < SecurityGroup + class SecurityGroupIPTables < VNMNetwork::SecurityGroup def initialize(vm, nic, sg_id, rules) super @@ -157,7 +157,7 @@ module SGIPTables # - :iptables_s # - :ipset_list def self.info - commands = Commands.new + commands = VNMNetwork::Commands.new commands << :iptables "-S" iptables_s = commands.run! @@ -188,7 +188,7 @@ module SGIPTables return if info[:iptables_s].split("\n").include? "-N #{GLOBAL_CHAIN}" - commands = Commands.new + commands = VNMNetwork::Commands.new commands.iptables "-N #{GLOBAL_CHAIN}" commands.iptables "-A FORWARD -m physdev --physdev-is-bridged -j #{GLOBAL_CHAIN}" @@ -244,7 +244,7 @@ module SGIPTables # IP spoofing # iptables -A one-3-0-o ! --source 10.0.0.1 -j DROP def self.nic_pre(vm, nic) - commands = Commands.new + commands = VNMNetwork::Commands.new vars = SGIPTables.vars(vm, nic) @@ -285,7 +285,7 @@ module SGIPTables chain_in = vars[:chain_in] chain_out = vars[:chain_out] - commands = Commands.new + commands = VNMNetwork::Commands.new commands << :iptables "-A #{chain_in} -j DROP" commands << :iptables "-A #{chain_out} -j DROP" @@ -304,7 +304,7 @@ module SGIPTables iptables_s = info[:iptables_s] ipset_list = info[:ipset_list] - commands = Commands.new + commands = VNMNetwork::Commands.new iptables_forwards.lines.reverse_each do |line| fields = line.split diff --git a/src/vnm_mad/remotes/lib/vm.rb b/src/vnm_mad/remotes/lib/vm.rb new file mode 100644 index 0000000000..8dfa9f5f18 --- /dev/null +++ b/src/vnm_mad/remotes/lib/vm.rb @@ -0,0 +1,110 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +module VNMMAD + +module VNMNetwork + + ############################################################################ + # This class represents the VM abstraction. It provides basic methods + # to interact with its network interfaces. + ############################################################################ + class VM + attr_accessor :nics, :vm_info, :deploy_id, :vm_root + + + # Creates a new VM object, and bootstrap the NICs array + # @param vm_root [REXML] XML document representing the VM + # @param xpath_filer [String] to get the VM NICs + # @param deploy_id [String] refers to the VM in the hypervisor + # @param hypervisor [String] + def initialize(vm_root, xpath_filter, deploy_id, hypervisor) + @vm_root = vm_root + @xpath_filter = xpath_filter + @deploy_id = deploy_id + @hypervisor = hypervisor + @vm_info = Hash.new + + @deploy_id = nil if deploy_id == "-" + + nics = VNMNetwork::Nics.new(@hypervisor) + + @vm_root.elements.each(@xpath_filter) do |nic_element| + nic = nics.new_nic + + nic_build_hash(nic_element,nic) + + nic.get_info(self) + nic.get_tap(self) + + nics << nic + end + + @nics = nics + end + + # Iterator on each NIC of the VM + def each_nic(block) + if @nics != nil + @nics.each do |the_nic| + block.call(the_nic) + end + end + end + + # Access an XML Element of the VM + # @param element [String] element name + # @return [String] valule of the element or nil if not found + def [](element) + if @vm_root + val = @vm_root.elements[element] + return val.text if !val.nil? && val.text + end + + nil + end + + private: + + # Method to build the associated Hash from a NIC + # @param nic_element [REXML] for the NIC + # @param nic [Nic] class representation + def nic_build_hash(nic_element,nic) + nic_element.elements.each('*') do |nic_attribute| + key = nic_attribute.name.downcase.to_sym + + if nic_attribute.has_elements? + data = {} + nic_build_hash(nic_attribute,data) + else + data = nic_attribute.text + end + + if nic[key] + if nic[key].instance_of?(Array) + nic[key] << data + else + nic[key] = [nic[key], data] + end + else + nic[key] = data + end + end + end + end +end + +end \ No newline at end of file From 536be6ecf0fc5ff9a8650ed700d40096201f8a22 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Mon, 22 Dec 2014 19:10:30 +0100 Subject: [PATCH 04/14] feature #3175: Moved firewall and SG drivers to new module. Removed old files --- install.sh | 14 +- src/vnm_mad/remotes/Firewall.rb | 180 ------ src/vnm_mad/remotes/IPNetmask.rb | 198 ------ src/vnm_mad/remotes/OpenNebulaNetwork.rb | 248 -------- src/vnm_mad/remotes/OpenNebulaNic.rb | 136 ---- src/vnm_mad/remotes/SecurityGroups.rb | 597 ------------------ src/vnm_mad/remotes/lib/one_firewall.rb | 198 ++++++ src/vnm_mad/remotes/lib/one_sg.rb | 107 ++++ src/vnm_mad/remotes/lib/opennebula_network.rb | 10 +- 9 files changed, 319 insertions(+), 1369 deletions(-) delete mode 100644 src/vnm_mad/remotes/Firewall.rb delete mode 100644 src/vnm_mad/remotes/IPNetmask.rb delete mode 100644 src/vnm_mad/remotes/OpenNebulaNetwork.rb delete mode 100644 src/vnm_mad/remotes/OpenNebulaNic.rb delete mode 100644 src/vnm_mad/remotes/SecurityGroups.rb create mode 100644 src/vnm_mad/remotes/lib/one_firewall.rb create mode 100644 src/vnm_mad/remotes/lib/one_sg.rb diff --git a/install.sh b/install.sh index ed8d0231d7..5edb65855c 100755 --- a/install.sh +++ b/install.sh @@ -932,12 +932,16 @@ AUTH_PLAIN_FILES="src/authm_mad/remotes/plain/authenticate" # Virtual Network Manager drivers to be installed under $REMOTES_LOCATION/vnm #------------------------------------------------------------------------------- -NETWORK_FILES="src/vnm_mad/remotes/OpenNebulaNetwork.rb \ +NETWORK_FILES="src/vnm_mad/remotes/lib/opennebula_network.rb \ src/vnm_mad/remotes/OpenNebulaNetwork.conf \ - src/vnm_mad/remotes/Firewall.rb \ - src/vnm_mad/remotes/SecurityGroups.rb \ - src/vnm_mad/remotes/IPNetmask.rb \ - src/vnm_mad/remotes/OpenNebulaNic.rb" + src/vnm_mad/remotes/lib/one_firewall.rb \ + src/vnm_mad/remotes/lib/one_sg.rb \ + src/vnm_mad/remotes/lib/address.rb \ + src/vnm_mad/remotes/lib/command.rb \ + src/vnm_mad/remotes/lib/command.rb \ + src/vnm_mad/remotes/lib/security_groups.rb \ + src/vnm_mad/remotes/lib/security_groups_iptables.rb \ + src/vnm_mad/remotes/lib/nic.rb" NETWORK_8021Q_FILES="src/vnm_mad/remotes/802.1Q/clean \ src/vnm_mad/remotes/802.1Q/post \ diff --git a/src/vnm_mad/remotes/Firewall.rb b/src/vnm_mad/remotes/Firewall.rb deleted file mode 100644 index 9440eb474b..0000000000 --- a/src/vnm_mad/remotes/Firewall.rb +++ /dev/null @@ -1,180 +0,0 @@ -# -------------------------------------------------------------------------- # -# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # -# # -# Licensed under the Apache License, Version 2.0 (the "License"); you may # -# not use this file except in compliance with the License. You may obtain # -# a copy of the License at # -# # -# http://www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -#--------------------------------------------------------------------------- # - -class OpenNebulaFirewall < OpenNebulaNetwork - DRIVER = "fw" - - XPATH_FILTER = "TEMPLATE/NIC[ICMP|WHITE_PORTS_TCP|WHITE_PORTS_UDP|" << - "BLACK_PORTS_TCP|BLACK_PORTS_UDP]" - - def initialize(vm, deploy_id = nil, hypervisor = nil) - super(vm,XPATH_FILTER,deploy_id,hypervisor) - @locking = true - end - - def activate - lock - - vm_id = @vm['ID'] - process do |nic| - #:white_ports_tcp => iptables_range - #:white_ports_udp => iptables_range - #:black_ports_tcp => iptables_range - #:black_ports_udp => iptables_range - #:icmp => 'DROP' or 'NO' - - nic_rules = Array.new - - chain = "one-#{vm_id}-#{nic[:network_id]}" - tap = nic[:tap] - - next if chain_exists?(chain) - - if tap - #TCP - if range = nic[:white_ports_tcp] - nic_rules << filter_established(chain, :tcp, :accept) - nic_rules << filter_ports(chain, :tcp, range, :accept) - nic_rules << filter_protocol(chain, :tcp, :drop) - elsif range = nic[:black_ports_tcp] - nic_rules << filter_ports(chain, :tcp, range, :drop) - end - - #UDP - if range = nic[:white_ports_udp] - nic_rules << filter_established(chain, :udp, :accept) - nic_rules << filter_ports(chain, :udp, range, :accept) - nic_rules << filter_protocol(chain, :udp, :drop) - elsif range = nic[:black_ports_udp] - nic_rules << filter_ports(chain, :udp, range, :drop) - end - - #ICMP - if nic[:icmp] - if %w(no drop).include? nic[:icmp].downcase - nic_rules << filter_established(chain, :icmp, :accept) - nic_rules << filter_protocol(chain, :icmp, :drop) - end - end - - process_chain(chain, tap, nic_rules) - end - end - - unlock - end - - def deactivate - lock - - vm_id = @vm['ID'] - process do |nic| - chain = "one-#{vm_id}-#{nic[:network_id]}" - iptables_out = `#{COMMANDS[:iptables]} -n -v --line-numbers -L FORWARD` - if m = iptables_out.match(/.*#{chain}.*/) - rule_num = m[0].split(/\s+/)[0] - purge_chain(chain, rule_num) - end - end - - unlock - end - - def purge_chain(chain, rule_num) - rules = Array.new - rules << rule("-D FORWARD #{rule_num}") - rules << rule("-F #{chain}") - rules << rule("-X #{chain}") - run_rules rules - end - - def process_chain(chain, tap, nic_rules) - rules = Array.new - if !nic_rules.empty? - # new chain - rules << new_chain(chain) - # move tap traffic to chain - rules << tap_to_chain(tap, chain) - - rules << nic_rules - end - run_rules rules - end - - def filter_established(chain, protocol, policy) - policy = policy.to_s.upcase - rule "-A #{chain} -p #{protocol} -m state --state ESTABLISHED -j #{policy}" - end - - def run_rules(rules) - rules.flatten.each do |rule| - OpenNebula.exec_and_log(rule) - end - end - - def range?(range) - range.match(/^(?:(?:\d+|\d+:\d+),)*(?:\d+|\d+:\d+)$/) - end - - def filter_protocol(chain, protocol, policy) - policy = policy.to_s.upcase - rule "-A #{chain} -p #{protocol} -j #{policy}" - end - - def filter_ports(chain, protocol, range, policy) - policy = policy.to_s.upcase - range.gsub!(/\s+/,"") - - if range? range - rule "-A #{chain} -p #{protocol} -m multiport --dports #{range} -j #{policy}" - end - end - - def tap_to_chain(tap, chain) - iptables_out = `#{COMMANDS[:iptables]} -n -v --line-numbers -L FORWARD` - - # Insert the rule on top of the 'opennebula' chain if it exists, so it - # doesn't conflict with the security groups driver - index = nil - iptables_out.lines.each do |line| - fields = line.split - if fields.include?("opennebula") && fields.include?("--physdev-is-bridged") - index = fields[0] - break - end - end - - if index - rule "-I FORWARD #{index} -m physdev --physdev-out #{tap} -j #{chain}" - else - rule "-A FORWARD -m physdev --physdev-out #{tap} -j #{chain}" - end - end - - def new_chain(chain) - rule "-N #{chain}" - end - - def chain_exists?(chain) - iptables_nl =`#{COMMANDS[:iptables]} -nL` - chains = iptables_nl.scan(/(one-.*?) .*references/).flatten - chains.include? chain - end - - def rule(rule) - "#{COMMANDS[:iptables]} #{rule}" - end -end diff --git a/src/vnm_mad/remotes/IPNetmask.rb b/src/vnm_mad/remotes/IPNetmask.rb deleted file mode 100644 index e112ab391d..0000000000 --- a/src/vnm_mad/remotes/IPNetmask.rb +++ /dev/null @@ -1,198 +0,0 @@ -# -------------------------------------------------------------------------- # -# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # -# # -# Licensed under the Apache License, Version 2.0 (the "License"); you may # -# not use this file except in compliance with the License. You may obtain # -# a copy of the License at # -# # -# http://www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -#--------------------------------------------------------------------------- # - -################################################################################ -# IP and NETMASK Library -################################################################################ - -class IP - include Comparable - attr_accessor :ip - - def initialize(ip) - @ip = ip - end - - def to_s - @ip - end - - def to_i - @ip.split(".").inject(0) {|t,e| (t << 8) + e.to_i } - end - - def to_hex - "0x" + to_i.to_s(16).rjust(8, '0') - end - - def to_bin - "0b" + to_i.to_s(2).rjust(16, '0') - end - - def to_hex_groups(p="") - to_i.to_s(16).rjust(8, '0').scan(/.{2}/).collect{|e| p+e}.join('.') - end - - def to_bin_groups(p="") - to_i.to_s(2).rjust(16, '0').scan(/.{8}/).collect{|e| p+e}.join('.') - end - - def self.from_i(i) - ip = 3.downto(0).collect {|s| (i >> 8*s) & 0xff }.join('.') - self.new(ip) - end - - def &(another_ip) - IP.from_i(self.to_i & another_ip.to_i) - end - - def +(size) - IP.from_i(self.to_i + size) - end - - def -(e) - if e.instance_of? Fixnum - IP.from_i(self.to_i - e) - else - e.to_i - self.to_i - end - end - - def <=>(another_ip) - self.to_i <=> another_ip.to_i - end -end - -class Netmask < IP - def self.from_cidr(cidr) - self.from_i(0xffffffff ^ 2**(32-cidr)-1) - end - - def to_cidr - 32 - Math.log((to_i ^ 0xffffffff) + 1, 2).to_i - end -end - -class Net - attr_accessor :ip, :netmask - - def initialize(ip, netmask = nil) - if netmask - @ip, @netmask = ip, netmask - else - ip, netmask = ip.split('/') - @ip = IP.new(ip) - @netmask = Netmask.from_cidr(netmask.to_i) if netmask - end - - @network_address = network_address - @last_address = last_address - end - - def network_address - IP.from_i(@ip.to_i & @netmask.to_i) - end - - def last_address - IP.from_i(@ip.to_i | (@netmask.to_i ^ 0xffffffff)) - end - - def info - s = "" - s << @network_address.to_s.ljust(15) - s << " /" - s << @netmask.to_cidr.to_s.rjust(2) - s << " " - s << @network_address.to_s.ljust(15) - s << " " - s << last_address.to_s.ljust(15) - - s - end - - def to_s - "#{@network_address}/#{@netmask.to_cidr}" - end - - def next_net - next_ip = IP.from_i(last_address.to_i + 1) - Net.new(next_ip, @netmask) - end - - def between?(ip_start, ip_end) - network_address >= ip_start && last_address <= ip_end - end -end - -class Range - def initialize(ip_start, size) - @ip_start = IP.new(ip_start) - @ip_end = @ip_start + size - end - - def get_nets - self.class.get_nets(@ip_start, @ip_end) - end - - def largest_subnet - self.class.largest_subnet(@ip_start, @ip_end) - end - - def self.get_nets(ip_start, ip_end) - nets = [] - - net_m = largest_subnet(ip_start, ip_end) - - # left scraps - if ip_start < net_m.network_address - nets.concat get_nets(ip_start, net_m.network_address - 1) - end - - nets << net_m - - # right scraps - if net_m.last_address < ip_end - nets.concat get_nets(net_m.last_address + 1, ip_end) - end - - nets - end - - def self.largest_subnet(ip_start, ip_end) - size = ip_start - ip_end - - # start with the largest subnet - if size > 0 - cidr = 32 - Math.log(size, 2).floor - else - cidr = 32 - end - - fits = false - - while !fits - net = Net.new(ip_start, Netmask.from_cidr(cidr)) - net = net.next_net if ip_start > net.network_address - - cidr += 1 - break if cidr > 32 - - fits = net.between?(ip_start, ip_end) - end - - net - end -end diff --git a/src/vnm_mad/remotes/OpenNebulaNetwork.rb b/src/vnm_mad/remotes/OpenNebulaNetwork.rb deleted file mode 100644 index fdff12df33..0000000000 --- a/src/vnm_mad/remotes/OpenNebulaNetwork.rb +++ /dev/null @@ -1,248 +0,0 @@ -# -------------------------------------------------------------------------- # -# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # -# # -# Licensed under the Apache License, Version 2.0 (the "License"); you may # -# not use this file except in compliance with the License. You may obtain # -# a copy of the License at # -# # -# http://www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -#--------------------------------------------------------------------------- # - -$: << File.dirname(__FILE__) -$: << File.join(File.dirname(__FILE__), '..') - -require 'rexml/document' -require 'base64' -require 'yaml' - -require 'OpenNebulaNic' - -require 'scripts_common' - -include OpenNebula - -begin - CONF = YAML.load_file( - File.join(File.dirname(__FILE__), "OpenNebulaNetwork.conf") - ) -rescue - CONF = { - :start_vlan => 2 - } -end - -def get_xen_command - if system("ps axuww | grep -v grep | grep '\\bxen\\b'") - "sudo xm" - else - "sudo xl" - end -end - -COMMANDS = { - :ebtables => "sudo ebtables", - :iptables => "sudo iptables", - :brctl => "sudo brctl", - :ip => "sudo ip", - :virsh => "virsh -c qemu:///system", - :xm => get_xen_command, - :ovs_vsctl=> "sudo ovs-vsctl", - :ovs_ofctl=> "sudo ovs-ofctl", - :lsmod => "lsmod", - :ipset => "sudo ipset" -} - -# Set PATH -ENV['PATH'] = "#{ENV['PATH']}:/bin:/sbin:/usr/bin" - -class VM - attr_accessor :nics, :vm_info, :deploy_id, :vm_root - - def nic_build_hash(nic_element,nic) - nic_element.elements.each('*') do |nic_attribute| - key = nic_attribute.name.downcase.to_sym - - - if nic_attribute.has_elements? - data = {} - nic_build_hash(nic_attribute,data) - else - data = nic_attribute.text - end - - if nic[key] - if nic[key].instance_of?(Array) - nic[key] << data - else - nic[key] = [nic[key], data] - end - else - nic[key] = data - end - end - end - - def initialize(vm_root, xpath_filter, deploy_id, hypervisor) - @vm_root = vm_root - @xpath_filter = xpath_filter - @deploy_id = deploy_id - @hypervisor = hypervisor - @vm_info = Hash.new - - @deploy_id = nil if deploy_id == "-" - - nics = Nics.new(@hypervisor) - - @vm_root.elements.each(@xpath_filter) do |nic_element| - nic = nics.new_nic - - nic_build_hash(nic_element,nic) - - nic.get_info(self) - nic.get_tap(self) - - nics << nic - end - - @nics = nics - end - - def each_nic(block) - if @nics != nil - @nics.each do |the_nic| - block.call(the_nic) - end - end - end - - def [](element) - if @vm_root - val = @vm_root.elements[element] - if !val.nil? and val.text - return val.text - end - end - nil - end -end - -class OpenNebulaNetwork - attr_reader :hypervisor, :vm - - def self.from_base64(vm_64, deploy_id = nil, hypervisor = nil) - vm_xml = Base64::decode64(vm_64) - self.new(vm_xml, deploy_id, hypervisor) - end - - def self.filter_driver(vm_64, deploy_id = nil, hypervisor = nil) - vm_xml = Base64::decode64(vm_64) - - if self.has_fw_attrs?(vm_xml) - OpenNebulaFirewall.new(vm_xml, deploy_id, hypervisor) - else - OpenNebulaSG.new(vm_xml, deploy_id, hypervisor) - end - end - - def initialize(vm_tpl, xpath_filter, deploy_id = nil, hypervisor = nil) - @locking = false - - if !hypervisor - @hypervisor = detect_hypervisor - else - @hypervisor = hypervisor - end - - @vm = VM.new(REXML::Document.new(vm_tpl).root, xpath_filter, deploy_id, @hypervisor) - end - - def lock - if @locking - driver_name = self.class.name.downcase - @locking_file = File.open("/tmp/onevnm-#{driver_name}-lock","w") - @locking_file.flock(File::LOCK_EX) - end - end - - def unlock - if @locking - @locking_file.close - end - end - - def process(&block) - @vm.each_nic(block) - end - - def detect_hypervisor - lsmod = `#{COMMANDS[:lsmod]}` - xen_file = "/proc/xen/capabilities" - - if File.exists?(xen_file) - "xen" - elsif lsmod.match(/kvm/) - "kvm" - else - nil - end - end - - def get_interfaces - bridges = Hash.new - brctl_exit =`#{COMMANDS[:brctl]} show` - - cur_bridge = "" - - brctl_exit.split("\n")[1..-1].each do |l| - l = l.split - - if l.length > 1 - cur_bridge = l[0] - - bridges[cur_bridge] = Array.new - bridges[cur_bridge] << l[3] if l[3] - else - bridges[cur_bridge] << l[0] - end - end - - bridges - end -end - -# Dynamic factory method for the filter class -require 'Firewall' -require 'SecurityGroups' -class OpenNebulaNetwork - # Returns a filter object based on the contents of the template - # - # @return OpenNebulaFirewall or OpenNebulaSG object - def self.filter_driver(vm_64, deploy_id = nil, hypervisor = nil) - vm_xml = Base64::decode64(vm_64) - - if self.has_fw_attrs?(vm_xml) - OpenNebulaFirewall.new(vm_xml, deploy_id, hypervisor) - else - OpenNebulaSG.new(vm_xml, deploy_id, hypervisor) - end - end - - # Returns true if the template contains the deprecated firewall attributes: - # - ICMP - # - WHITE_PORTS_TCP - # - WHITE_PORTS_UDP - # - BLACK_PORTS_TCP - # - BLACK_PORTS_UDP - # - # @return Boolean - def self.has_fw_attrs?(vm_xml) - vm_root = REXML::Document.new(vm_xml).root - !vm_root.elements[OpenNebulaFirewall::XPATH_FILTER].nil? - end -end diff --git a/src/vnm_mad/remotes/OpenNebulaNic.rb b/src/vnm_mad/remotes/OpenNebulaNic.rb deleted file mode 100644 index 24b4ee7643..0000000000 --- a/src/vnm_mad/remotes/OpenNebulaNic.rb +++ /dev/null @@ -1,136 +0,0 @@ -# -------------------------------------------------------------------------- # -# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # -# # -# Licensed under the Apache License, Version 2.0 (the "License"); you may # -# not use this file except in compliance with the License. You may obtain # -# a copy of the License at # -# # -# http://www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -#--------------------------------------------------------------------------- # - -# This class represents the NICS of a VM -class Nics < Array - def initialize(hypervisor) - case hypervisor - when "kvm" - @nicClass = NicKVM - when "xen" - @nicClass = NicXen - when "vmware" - @nicClass = NicVMware - end - end - - def new_nic - @nicClass.new - end -end - -# A NIC using KVM. This class implements functions to get the physical interface -# that the NIC is using -class NicKVM < Hash - def initialize - super(nil) - end - - def get_info(vm) - if vm.deploy_id - deploy_id = vm.deploy_id - else - deploy_id = vm['DEPLOY_ID'] - end - - if deploy_id and vm.vm_info[:dumpxml].nil? - vm.vm_info[:dumpxml] = `#{COMMANDS[:virsh]} dumpxml #{deploy_id} \ - 2>/dev/null` - - vm.vm_info.each_key do |k| - vm.vm_info[k] = nil if vm.vm_info[k].to_s.strip.empty? - end - end - end - - def get_tap(vm) - dumpxml = vm.vm_info[:dumpxml] - - if dumpxml - dumpxml_root = REXML::Document.new(dumpxml).root - - xpath = "devices/interface[@type='bridge']/" - xpath << "mac[@address='#{self[:mac]}']/../target" - tap = dumpxml_root.elements[xpath] - - if tap - self[:tap] = tap.attributes['dev'] - end - end - self - end -end - - -# A NIC using Xen. This class implements functions to get the physical interface -# that the NIC is using -class NicXen < Hash - def initialize - super(nil) - end - - def get_info(vm) - if vm.deploy_id - deploy_id = vm.deploy_id - else - deploy_id = vm['DEPLOY_ID'] - end - - if deploy_id and (vm.vm_info[:domid].nil? or vm.vm_info[:networks].nil?) - vm.vm_info[:domid] =`#{COMMANDS[:xm]} domid #{deploy_id}`.strip - vm.vm_info[:networks] =`#{COMMANDS[:xm]} network-list #{deploy_id}` - - vm.vm_info.each_key do |k| - vm.vm_info[k] = nil if vm.vm_info[k].to_s.strip.empty? - end - end - end - - def get_tap(vm) - domid = vm.vm_info[:domid] - - if domid - networks = vm.vm_info[:networks].split("\n")[1..-1] - networks.each do |net| - n = net.split - - iface_id = n[0] - iface_mac = n[2] - - if iface_mac == self[:mac] - self[:tap] = "vif#{domid}.#{iface_id}" - break - end - end - end - self - end -end - -# A NIC using VMware. This class implements functions to get the physical interface -# that the NIC is using -class NicVMware < Hash - def initialize - super(nil) - end - - def get_info(vm) - end - - def get_tap(vm) - self - end -end diff --git a/src/vnm_mad/remotes/SecurityGroups.rb b/src/vnm_mad/remotes/SecurityGroups.rb deleted file mode 100644 index c79edeb4a8..0000000000 --- a/src/vnm_mad/remotes/SecurityGroups.rb +++ /dev/null @@ -1,597 +0,0 @@ -# -------------------------------------------------------------------------- # -# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # -# # -# Licensed under the Apache License, Version 2.0 (the "License"); you may # -# not use this file except in compliance with the License. You may obtain # -# a copy of the License at # -# # -# http://www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -#--------------------------------------------------------------------------- # - -require 'IPNetmask' - -################################################################################ -# SecurityGroups and Rules -################################################################################ - -class CommandsError < StandardError; end - -class Commands - def initialize - clear! - end - - def add(cmd) - if cmd.instance_of?(String) - @commands << cmd - else - @commands.concat(cmd.to_a) - end - end - - def method_missing(m, *args, &block) - if COMMANDS.keys.include?(m) - @commands << "#{COMMANDS[m]} #{args.join(' ')}" - else - super - end - end - - def run! - out = "" - - @commands.each{|c| - - out << `#{c}` - - if !$?.success? - clear! - raise CommandsError.new(c), "Command Error: #{c}" - end - } - - clear! - out - end - - def uniq! - @commands.uniq! - end - - def clear! - @commands = [] - end - - def to_a - @commands - end -end - -class RuleError < StandardError; end - -class Rule - TYPES = { - # PROTOCOL, RULE_TYPE, NET, RANGE, ICMP_TYPE - [ 1, 1, 0, 0, 0 ] => :protocol, - [ 1, 1, 0, 1, 0 ] => :portrange, - [ 1, 1, 0, 0, 1 ] => :icmp_type, - [ 1, 1, 1, 0, 0 ] => :net, - [ 1, 1, 1, 1, 0 ] => :net_portrange, - [ 1, 1, 1, 0, 1 ] => :net_icmp_type - } - - ICMP_TYPES = %w{3 5 11 12 0 4 8 9 10 13 14 17 18} - - ICMP_TYPES_EXPANDED = { - 3 => [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15], - 5 => [0, 1, 2, 3], - 11 => [0, 1], - 12 => [0, 1] - } - - def initialize(rule) - @rule = rule - @commands = Commands.new - - if !valid? - raise RuleError.new, "Invalid Rule: #{error_message}" - end - end - - # Getters - def protocol - p = @rule[:protocol].downcase.to_sym rescue nil - - if p == :ipsec - :esp - else - p - end - end - - def rule_type - @rule[:rule_type].downcase.to_sym rescue nil - end - - def range - @rule[:range] - end - - def net - return nil if @rule[:ip].nil? || @rule[:size].nil? - - r = Range.new(@rule[:ip], @rule[:size].to_i) - r.get_nets.collect{|n| n.to_s} - end - - def icmp_type - @rule[:icmp_type] - end - - def icmp_type_expand - if (codes = ICMP_TYPES_EXPANDED[icmp_type.to_i]) - codes.collect{|e| "#{icmp_type}/#{e}"} - else - ["#{icmp_type}/0"] - end - end - - # Helper - - def valid? - valid = true - error_message = [] - - if type.nil? - error_message << "Invalid combination of rule attributes: " - error_message << type(true).to_s - valid = false - end - - if !protocol || ![:all, :tcp, :udp, :icmp, :esp].include?(protocol) - error_message << "Invalid protocol: #{protocol}" - valid = false - end - - if !rule_type || ![:inbound, :outbound].include?(rule_type) - error_message << "Invalid rule_type: #{rule_type}" - valid = false - end - - if range && !range.match(/^(?:(?:\d+|\d+:\d+),)*(?:\d+|\d+:\d+)$/) - error_message << "Invalid range: #{range}" - valid = false - end - - if icmp_type && !ICMP_TYPES.include?(icmp_type) - error_message << "ICMP Type '#{icmp_type}' not supported. Valid list is '#{ICMP_TYPES.join(',')}'" - end - - if icmp_type && !(protocol == :icmp) - error_message << "Protocol '#{protocol}' does not support ICMP TYPES" - valid = false - end - - if range && ![:tcp, :udp].include?(protocol) - error_message << "Protocol '#{protocol}' does not support port ranges" - valid = false - end - - if net && !valid_net? - error_message << "Invalid net: IP:'#{@rule[:ip]}' SIZE:'#{@rule[:size]}'" - valid = false - end - - return [valid, error_message.join("\n")] - end - - def valid_net? - @rule[:ip].match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) && \ - @rule[:size].match(/^\d+$/) - end - - # Returns the rule type. Rules currently support these (final and relevant) - # attributes. - # - # PROTOCOL (mandatory) - # - Specifies the protocol of the rule - # - values: ['ALL', 'TCP', 'UDP', 'ICMP', 'IPSEC'] - # - # RULE_TYPE (mandatory) - # - Specifies the direction of application of the rule - # - values: ['INBOUND', 'OUTBOUND'] - # - # RANGE (optional) - # - only works for protocols ['TCP', 'UDP'] - # - uses the iptables multiports syntax - # - # ICMP_TYPE (optional) - # - Only works for protocol 'ICMP' - # - Is either in the form of '' or '/', where both - # '' and '' are integers. This class has a helper method - # tgat expands '' into all the '/' subtypes. - # - # IP and SIZE (optional but must be specified together) - # - Can be applied to any protocol - # - IP is the first valid IP and SIZE is the number of consecutive IPs - # - # Depending on the combination of these attributes we can obtaine 4 rule - # rule types (some with subtypes): - # - # ['PROTOCOL', 'RULE_TYPE'] => Type 1: 'protocol' - # ['PROTOCOL', 'RULE_TYPE', 'RANGE'] => Type 2A: 'portrange' - # ['PROTOCOL', 'RULE_TYPE', 'ICMP_TYPE'] => Type 2B: 'icmp_type' - # ['PROTOCOL', 'RULE_TYPE', 'IP', 'SIZE'] => Type 3: 'net' - # ['PROTOCOL', 'RULE_TYPE', 'IP', 'SIZE', 'RANGE'] => Type 4A: 'net_portrange' - # ['PROTOCOL', 'RULE_TYPE', 'IP', 'SIZE', 'ICMP_TYPE'] => Type 4B: 'net_icmp_type' - # - # @return [Symbol] The rule type - def type(only_key = false) - key = [protocol, rule_type, net, range, icmp_type].collect do |e| - !!e ? 1 : 0 - end - - only_key ? key : TYPES[key] - end -end - -class SecurityGroup - def initialize(vm, nic, sg_id, rules) - @vm = vm - @nic = nic - @sg_id = sg_id - - @rules = [] - @invalid_rules = [] - - rules.each do |rule| - @rules << Rule.new(rule) - end if rules - end -end - -################################################################################ -# IPTables Implementation -################################################################################ - -class SecurityGroupIPTables < SecurityGroup - GLOBAL_CHAIN = "opennebula" - - def initialize(vm, nic, sg_id, rules) - super - - @commands = Commands.new - - @vars = self.class.vars(@vm, @nic, @sg_id) - - @chain_in = @vars[:chain_in] - @chain_out = @vars[:chain_out] - @set_sg_in = @vars[:set_sg_in] - @set_sg_out = @vars[:set_sg_out] - end - - def process_rules - @rules.each do |rule| - case rule.type - when :protocol - chain = rule.rule_type == :inbound ? @chain_in : @chain_out - @commands.iptables("-A #{chain} -p #{rule.protocol} -j RETURN") - - when :portrange - chain = rule.rule_type == :inbound ? @chain_in : @chain_out - @commands.iptables("-A #{chain} -p #{rule.protocol} -m multiport --dports #{rule.range} -j RETURN") - - when :icmp_type - chain = rule.rule_type == :inbound ? @chain_in : @chain_out - @commands.iptables("-A #{chain} -p icmp --icmp-type #{rule.icmp_type} -j RETURN") - - when :net - if rule.rule_type == :inbound - chain = @chain_in - set = "#{@set_sg_in}-#{rule.protocol}-n" - dir = "src" - else - chain = @chain_out - set = "#{@set_sg_out}-#{rule.protocol}-n" - dir = "dst" - end - - @commands.ipset("create #{set} hash:net") - @commands.iptables("-A #{chain} -p #{rule.protocol} -m set --match-set #{set} #{dir} -j RETURN") - - rule.net.each do |n| - @commands.ipset("add -exist #{set} #{n}") - end - - when :net_portrange - if rule.rule_type == :inbound - chain = @chain_in - set = @set_sg_in + "-nr" - dir = "src,dst" - else - chain = @chain_in - set = @set_sg_in + "-n" - dir = "dst,dst" - end - - @commands.ipset("create #{set} hash:net,port") - @commands.iptables("-A #{chain} -m set --match-set #{set} #{dir} -j RETURN") - - rule.net.each do |n| - rule.range.split(",").each do |r| - r.gsub!(":","-") - net_range = "#{n},#{rule.protocol}:#{r}" - @commands.ipset("add -exist #{set} #{net_range}") - end - end - - when :net_icmp_type - if rule.rule_type == :inbound - chain = @chain_in - set = @set_sg_in + "-nr" - dir = "src,dst" - else - chain = @chain_in - set = @set_sg_in + "-n" - dir = "dst,dst" - end - - @commands.ipset("create #{set} hash:net,port") - @commands.iptables("-A #{chain} -m set --match-set #{set} #{dir} -j RETURN") - - rule.net.each do |n| - rule.icmp_type_expand.each do |type_code| - net_range = "#{n},icmp:#{type_code}" - @commands.ipset("add -exist #{set} #{net_range}") - end if rule.icmp_type_expand - end - end - end - - @commands.uniq! - end - - def run! - @commands.run! - end - - ############################################################################ - # Static methods - ############################################################################ - - def self.global_bootstrap - info = self.info - - if !info[:iptables_s].split("\n").include? "-N #{GLOBAL_CHAIN}" - commands = Commands.new - - commands.iptables "-N #{GLOBAL_CHAIN}" - commands.iptables "-A FORWARD -m physdev --physdev-is-bridged -j #{GLOBAL_CHAIN}" - commands.iptables "-A #{GLOBAL_CHAIN} -j ACCEPT" - - commands.run! - end - end - - def self.nic_pre(vm, nic) - commands = Commands.new - - vars = self.vars(vm, nic) - - chain = vars[:chain] - chain_in = vars[:chain_in] - chain_out = vars[:chain_out] - - # create chains - commands.iptables "-N #{chain_in}" # inbound - commands.iptables "-N #{chain_out}" # outbound - - # Send traffic to the NIC chains - commands.iptables"-I #{GLOBAL_CHAIN} -m physdev --physdev-out #{nic[:tap]} --physdev-is-bridged -j #{chain_in}" - commands.iptables"-I #{GLOBAL_CHAIN} -m physdev --physdev-in #{nic[:tap]} --physdev-is-bridged -j #{chain_out}" - - # Mac-spofing - if nic[:filter_mac_spoofing] == "YES" - commands.iptables"-A #{chain_out} -m mac ! --mac-source #{nic[:mac]} -j DROP" - end - - # IP-spofing - if nic[:filter_ip_spoofing] == "YES" - commands.iptables"-A #{chain_out} ! --source #{nic[:ip]} -j DROP" - end - - # Related, Established - commands.iptables"-A #{chain_in} -m state --state ESTABLISHED,RELATED -j ACCEPT" - commands.iptables"-A #{chain_out} -m state --state ESTABLISHED,RELATED -j ACCEPT" - - commands.run! - end - - def self.nic_post(vm, nic) - vars = self.vars(vm, nic) - chain_in = vars[:chain_in] - chain_out = vars[:chain_out] - - commands = Commands.new - commands.iptables("-A #{chain_in} -j DROP") - commands.iptables("-A #{chain_out} -j DROP") - - commands.run! - end - - def self.nic_deactivate(vm, nic) - vars = self.vars(vm, nic) - chain = vars[:chain] - chain_in = vars[:chain_in] - chain_out = vars[:chain_out] - - info = self.info - iptables_forwards = info[:iptables_forwards] - iptables_s = info[:iptables_s] - ipset_list = info[:ipset_list] - - commands = Commands.new - - iptables_forwards.lines.reverse_each do |line| - fields = line.split - if [chain_in, chain_out].include?(fields[1]) - n = fields[0] - commands.iptables("-D #{GLOBAL_CHAIN} #{n}") - end - end - - remove_chains = [] - iptables_s.lines.each do |line| - if line.match(/^-N #{chain}/) - remove_chains << line.split[1] - end - end - remove_chains.each {|c| commands.iptables("-F #{c}") } - remove_chains.each {|c| commands.iptables("-X #{c}") } - - ipset_list.lines.each do |line| - if line.match(/^#{chain}/) - set = line.strip - commands.ipset("destroy #{set}") - end - end - - commands.run! - end - - def self.info - commands = Commands.new - - commands.iptables("-S") - iptables_s = commands.run! - - if iptables_s.match(/^-N #{GLOBAL_CHAIN}$/) - commands.iptables("-L #{GLOBAL_CHAIN} --line-numbers") - iptables_forwards = commands.run! - else - iptables_forwards = "" - end - - commands.ipset("list -name") - ipset_list = commands.run! - - { - :iptables_forwards => iptables_forwards, - :iptables_s => iptables_s, - :ipset_list => ipset_list - } - end - - - def self.vars(vm, nic, sg_id = nil) - vm_id = vm['ID'] - nic_id = nic[:nic_id] - - vars = {} - - vars[:vm_id] = vm_id, - vars[:nic_id] = nic_id, - vars[:chain] = "one-#{vm_id}-#{nic_id}", - vars[:chain_in] = "#{vars[:chain]}-i", - vars[:chain_out] = "#{vars[:chain]}-o" - - if sg_id - vars[:set_sg_in] = "#{vars[:chain]}-#{sg_id}-i" - vars[:set_sg_out] = "#{vars[:chain]}-#{sg_id}-o" - end - - vars - end -end - -################################################################################ -# OpenNebula Firewall with Security Groups Based on IPTables (KVM and Xen) -################################################################################ - -class OpenNebulaSG < OpenNebulaNetwork - DRIVER = "sg" - XPATH_FILTER = "TEMPLATE/NIC" - SECURITY_GROUP_CLASS = SecurityGroupIPTables - - def initialize(vm, deploy_id = nil, hypervisor = nil) - super(vm, XPATH_FILTER, deploy_id, hypervisor) - @locking = true - @commands = Commands.new - - get_security_group_rules - end - - def get_security_group_rules - rules = {} - @vm.vm_root.elements.each('TEMPLATE/SECURITY_GROUP_RULE') do |r| - security_group_rule = {} - - r.elements.each do |e| - key = e.name.downcase.to_sym - security_group_rule[key] = e.text - end - - id = security_group_rule[:security_group_id] - rules[id] = [] if rules[id].nil? - rules[id] << security_group_rule - end - @security_group_rules = rules - end - - def activate - deactivate - lock - - # Global Bootstrap - SECURITY_GROUP_CLASS.global_bootstrap - - # Process the rules - @vm.nics.each do |nic| - next if nic[:security_groups].nil? \ - && nic[:filter_mac_spoofing] != "YES" \ - && nic[:filter_ip_spoofing] != "YES" - - - SECURITY_GROUP_CLASS.nic_pre(@vm, nic) - - sg_ids = nic[:security_groups].split(",") - sg_ids.each do |sg_id| - rules = @security_group_rules[sg_id] - sg = SECURITY_GROUP_CLASS.new(@vm, nic, sg_id, rules) - - begin - sg.process_rules - sg.run! - rescue Exception => e - unlock - deactivate - raise e - end - end - - SECURITY_GROUP_CLASS.nic_post(@vm, nic) - end - - unlock - end - - def deactivate - lock - - begin - @vm.nics.each do |nic| - SECURITY_GROUP_CLASS.nic_deactivate(@vm, nic) - end - rescue Exception => e - raise e - ensure - unlock - end - end -end diff --git a/src/vnm_mad/remotes/lib/one_firewall.rb b/src/vnm_mad/remotes/lib/one_firewall.rb new file mode 100644 index 0000000000..a4b66eccbf --- /dev/null +++ b/src/vnm_mad/remotes/lib/one_firewall.rb @@ -0,0 +1,198 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +module VNMMAD + + ############################################################################ + # Filter network driver based on simple iptables rules + ############################################################################ + class OpenNebulaFirewall < VNMMAD::OpenNebulaNetwork + # Driver name + DRIVER = "fw" + + # NICs filter. Select NICs with one or more of ICMP, WHITE_PORTS_* or + # BLACK_PORTS_* + XPATH_FILTER = "TEMPLATE/NIC[ICMP|WHITE_PORTS_TCP|WHITE_PORTS_UDP|" << + "BLACK_PORTS_TCP|BLACK_PORTS_UDP]" + + # Creates the driver object. It sets locking to prevent race conditions + # between concurrent deployments + def initialize(vm, deploy_id = nil, hypervisor = nil) + super(vm,XPATH_FILTER,deploy_id,hypervisor) + @locking = true + end + + # Function to activate the driver in the VM + # + def activate + lock + + vm_id = @vm['ID'] + process do |nic| + #:white_ports_tcp => iptables_range + #:white_ports_udp => iptables_range + #:black_ports_tcp => iptables_range + #:black_ports_udp => iptables_range + #:icmp => 'DROP' or 'NO' + + nic_rules = Array.new + + chain = "one-#{vm_id}-#{nic[:network_id]}" + tap = nic[:tap] + + next if chain_exists?(chain) + + if tap + #TCP + if range = nic[:white_ports_tcp] + nic_rules << filter_established(chain, :tcp, :accept) + nic_rules << filter_ports(chain, :tcp, range, :accept) + nic_rules << filter_protocol(chain, :tcp, :drop) + elsif range = nic[:black_ports_tcp] + nic_rules << filter_ports(chain, :tcp, range, :drop) + end + + #UDP + if range = nic[:white_ports_udp] + nic_rules << filter_established(chain, :udp, :accept) + nic_rules << filter_ports(chain, :udp, range, :accept) + nic_rules << filter_protocol(chain, :udp, :drop) + elsif range = nic[:black_ports_udp] + nic_rules << filter_ports(chain, :udp, range, :drop) + end + + #ICMP + if nic[:icmp] + if %w(no drop).include? nic[:icmp].downcase + nic_rules << filter_established(chain, :icmp, :accept) + nic_rules << filter_protocol(chain, :icmp, :drop) + end + end + + process_chain(chain, tap, nic_rules) + end + end + + unlock + end + + def deactivate + lock + + vm_id = @vm['ID'] + process do |nic| + chain = "one-#{vm_id}-#{nic[:network_id]}" + iptables_out = `#{COMMANDS[:iptables]} -n -v --line-numbers -L FORWARD` + if m = iptables_out.match(/.*#{chain}.*/) + rule_num = m[0].split(/\s+/)[0] + purge_chain(chain, rule_num) + end + end + + unlock + end + + ######################################################################## + # Methods to deal with iptables rules + ######################################################################## + private: + + def purge_chain(chain, rule_num) + rules = Array.new + rules << rule("-D FORWARD #{rule_num}") + rules << rule("-F #{chain}") + rules << rule("-X #{chain}") + run_rules rules + end + + def process_chain(chain, tap, nic_rules) + rules = Array.new + if !nic_rules.empty? + # new chain + rules << new_chain(chain) + # move tap traffic to chain + rules << tap_to_chain(tap, chain) + + rules << nic_rules + end + run_rules rules + end + + def filter_established(chain, protocol, policy) + policy = policy.to_s.upcase + rule "-A #{chain} -p #{protocol} -m state --state ESTABLISHED -j #{policy}" + end + + def run_rules(rules) + rules.flatten.each do |rule| + OpenNebula.exec_and_log(rule) + end + end + + def range?(range) + range.match(/^(?:(?:\d+|\d+:\d+),)*(?:\d+|\d+:\d+)$/) + end + + def filter_protocol(chain, protocol, policy) + policy = policy.to_s.upcase + rule "-A #{chain} -p #{protocol} -j #{policy}" + end + + def filter_ports(chain, protocol, range, policy) + policy = policy.to_s.upcase + range.gsub!(/\s+/,"") + + if range? range + rule "-A #{chain} -p #{protocol} -m multiport --dports #{range} -j #{policy}" + end + end + + def tap_to_chain(tap, chain) + iptables_out = `#{COMMANDS[:iptables]} -n -v --line-numbers -L FORWARD` + + # Insert the rule on top of the 'opennebula' chain if it exists, so it + # doesn't conflict with the security groups driver + index = nil + iptables_out.lines.each do |line| + fields = line.split + if fields.include?("opennebula") && fields.include?("--physdev-is-bridged") + index = fields[0] + break + end + end + + if index + rule "-I FORWARD #{index} -m physdev --physdev-out #{tap} -j #{chain}" + else + rule "-A FORWARD -m physdev --physdev-out #{tap} -j #{chain}" + end + end + + def new_chain(chain) + rule "-N #{chain}" + end + + def chain_exists?(chain) + iptables_nl =`#{COMMANDS[:iptables]} -nL` + chains = iptables_nl.scan(/(one-.*?) .*references/).flatten + chains.include? chain + end + + def rule(rule) + "#{COMMANDS[:iptables]} #{rule}" + end + end +end \ No newline at end of file diff --git a/src/vnm_mad/remotes/lib/one_sg.rb b/src/vnm_mad/remotes/lib/one_sg.rb new file mode 100644 index 0000000000..ab698c6920 --- /dev/null +++ b/src/vnm_mad/remotes/lib/one_sg.rb @@ -0,0 +1,107 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +require 'opennebula_network' + +module VNMMAD + + ############################################################################ + # OpenNebula Firewall with Security Groups Based on IPTables (KVM and Xen) + ############################################################################ + class OpenNebulaSG < VNMMAD::OpenNebulaNetwork + + DRIVER = "sg" + XPATH_FILTER = "TEMPLATE/NIC" + + def initialize(vm, deploy_id = nil, hypervisor = nil) + super(vm, XPATH_FILTER, deploy_id, hypervisor) + @locking = true + @commands = Commands.new + + rules = {} + @vm.vm_root.elements.each('TEMPLATE/SECURITY_GROUP_RULE') do |r| + security_group_rule = {} + + r.elements.each do |e| + key = e.name.downcase.to_sym + security_group_rule[key] = e.text + end + + id = security_group_rule[:security_group_id] + + rules[id] = [] if rules[id].nil? + rules[id] << security_group_rule + end + + @security_group_rules = rules + end + + def activate + deactivate + lock + + # Global Bootstrap + VNMMAD::SGIPTables.global_bootstrap + + # Process the rules + @vm.nics.each do |nic| + next if nic[:security_groups].nil? \ + && nic[:filter_mac_spoofing] != "YES" \ + && nic[:filter_ip_spoofing] != "YES" + + + VNMMAD::SGIPTables.nic_pre(@vm, nic) + + sg_ids = nic[:security_groups].split(",") + + sg_ids.each do |sg_id| + rules = @security_group_rules[sg_id] + + sg = VNMMAD::SGIPTables::SecurityGroupIPTables.new(@vm, nic, + sg_id, rules) + + begin + sg.process_rules + sg.run! + rescue Exception => e + unlock + deactivate + raise e + end + end + + VNMMAD::SGIPTables.nic_post(@vm, nic) + end + + unlock + end + + def deactivate + lock + + begin + @vm.nics.each do |nic| + VNMMAD::SGIPTables.nic_deactivate(@vm, nic) + end + rescue Exception => e + raise e + ensure + unlock + end + end + end + +end diff --git a/src/vnm_mad/remotes/lib/opennebula_network.rb b/src/vnm_mad/remotes/lib/opennebula_network.rb index 350d9adfe8..d1a3bd4c3e 100644 --- a/src/vnm_mad/remotes/lib/opennebula_network.rb +++ b/src/vnm_mad/remotes/lib/opennebula_network.rb @@ -23,11 +23,11 @@ require 'yaml' require 'one_firewall' require 'one_sg' -require 'lib/vm' -require 'lib/nic' -require 'lib/address' -require 'lib/security_groups' -require 'lib/security_groups_iptables' +require 'vm' +require 'nic' +require 'address' +require 'security_groups' +require 'security_groups_iptables' require 'scripts_common' From 90c3e9ffb15a33e6edfb0d82feadf262d53ad361 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Mon, 22 Dec 2014 19:50:44 +0100 Subject: [PATCH 05/14] feature #3175: Fix multiple syntax errors --- install.sh | 3 +- src/vnm_mad/remotes/802.1Q/HostManaged.rb | 15 ++-- src/vnm_mad/remotes/lib/command.rb | 30 ++++---- src/vnm_mad/remotes/lib/one_firewall.rb | 10 +-- src/vnm_mad/remotes/lib/opennebula_network.rb | 37 +--------- src/vnm_mad/remotes/lib/security_groups.rb | 8 +-- .../remotes/lib/security_groups_iptables.rb | 72 +++++++++---------- src/vnm_mad/remotes/lib/vm.rb | 2 +- src/vnm_mad/remotes/lib/vnmmad.rb | 49 +++++++++++++ 9 files changed, 121 insertions(+), 105 deletions(-) create mode 100644 src/vnm_mad/remotes/lib/vnmmad.rb diff --git a/install.sh b/install.sh index 5edb65855c..3412a04599 100755 --- a/install.sh +++ b/install.sh @@ -933,12 +933,13 @@ AUTH_PLAIN_FILES="src/authm_mad/remotes/plain/authenticate" #------------------------------------------------------------------------------- NETWORK_FILES="src/vnm_mad/remotes/lib/opennebula_network.rb \ + src/vnm_mad/remotes/lib/vnmmad.rb \ src/vnm_mad/remotes/OpenNebulaNetwork.conf \ src/vnm_mad/remotes/lib/one_firewall.rb \ src/vnm_mad/remotes/lib/one_sg.rb \ src/vnm_mad/remotes/lib/address.rb \ src/vnm_mad/remotes/lib/command.rb \ - src/vnm_mad/remotes/lib/command.rb \ + src/vnm_mad/remotes/lib/vm.rb \ src/vnm_mad/remotes/lib/security_groups.rb \ src/vnm_mad/remotes/lib/security_groups_iptables.rb \ src/vnm_mad/remotes/lib/nic.rb" diff --git a/src/vnm_mad/remotes/802.1Q/HostManaged.rb b/src/vnm_mad/remotes/802.1Q/HostManaged.rb index b6883661d6..88bd38af1f 100644 --- a/src/vnm_mad/remotes/802.1Q/HostManaged.rb +++ b/src/vnm_mad/remotes/802.1Q/HostManaged.rb @@ -14,9 +14,9 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -require 'OpenNebulaNetwork' +require 'vnmmad' -class OpenNebulaHM < OpenNebulaNetwork +class OpenNebulaHM < VNMMAD::OpenNebulaNetwork DRIVER = "802.1Q" XPATH_FILTER = "TEMPLATE/NIC[VLAN='YES']" @@ -34,6 +34,7 @@ class OpenNebulaHM < OpenNebulaNetwork lock vm_id = @vm['ID'] + process do |nic| bridge = nic[:bridge] dev = nic[:phydev] @@ -71,18 +72,18 @@ class OpenNebulaHM < OpenNebulaNetwork end def create_bridge(bridge) - OpenNebula.exec_and_log("#{COMMANDS[:brctl]} addbr #{bridge}") + OpenNebula.exec_and_log("#{VNMMAD::COMMANDS[:brctl]} addbr #{bridge}") @bridges[bridge] = Array.new end def device_exists?(dev, vlan=nil) dev = "#{dev}.#{vlan}" if vlan - `#{COMMANDS[:ip]} link show #{dev}` + `#{VNMMAD::COMMANDS[:ip]} link show #{dev}` $?.exitstatus == 0 end def create_dev_vlan(dev, vlan) - cmd = "#{COMMANDS[:ip]} link add link #{dev}" + cmd = "#{VNMMAD::COMMANDS[:ip]} link add link #{dev}" cmd << " name #{dev}.#{vlan} type vlan id #{vlan}" OpenNebula.exec_and_log(cmd) @@ -96,12 +97,12 @@ class OpenNebulaHM < OpenNebulaNetwork def attach_brigde_dev(bridge, dev, vlan=nil) dev = "#{dev}.#{vlan}" if vlan - OpenNebula.exec_and_log("#{COMMANDS[:brctl]} addif #{bridge} #{dev}") + OpenNebula.exec_and_log("#{VNMMAD::COMMANDS[:brctl]} addif #{bridge} #{dev}") @bridges[bridge] << dev end def ifup(dev, vlan=nil) dev = "#{dev}.#{vlan}" if vlan - OpenNebula.exec_and_log("#{COMMANDS[:ip]} link set #{dev} up") + OpenNebula.exec_and_log("#{VNMMAD::COMMANDS[:ip]} link set #{dev} up") end end diff --git a/src/vnm_mad/remotes/lib/command.rb b/src/vnm_mad/remotes/lib/command.rb index 3242283d29..d1e65a3de7 100644 --- a/src/vnm_mad/remotes/lib/command.rb +++ b/src/vnm_mad/remotes/lib/command.rb @@ -18,6 +18,20 @@ module VNMMAD module VNMNetwork + # This module include implementation specific functions. It MUST not be + # be used in other VNMAD classes. + module Configuration + # Return the command to talk to the Xen hypervisor xm or xl for + # Xen 3 and 4 + def self.get_xen_command + if system("ps axuww | grep -v grep | grep '\\bxen\\b'") + "sudo xm" + else + "sudo xl" + end + end + end + # Command configuration for common network commands. This CAN be adjust # to local installations. Any modification requires to sync the hosts with # onehost sync command. @@ -41,7 +55,7 @@ module VNMNetwork # Adds a new command to the command array # @param cmd [String] the command, it can be a key defined in COMMANDS # @para args[Array] Arguments for the command - def << (cmd, *args) + def add (cmd, *args) if COMMANDS.keys.include?(cmd.to_sym) cmd_str = "#{COMMANDS[cmd.to_sym]} #{args.join(' ')}" else @@ -67,20 +81,6 @@ module VNMNetwork return out end end - - # This module include implementation specific functions. It MUST not be - # be used in other VNMAD classes. - module Configuration - # Return the command to talk to the Xen hypervisor xm or xl for - # Xen 3 and 4 - def self.get_xen_command - if system("ps axuww | grep -v grep | grep '\\bxen\\b'") - "sudo xm" - else - "sudo xl" - end - end - end end end \ No newline at end of file diff --git a/src/vnm_mad/remotes/lib/one_firewall.rb b/src/vnm_mad/remotes/lib/one_firewall.rb index a4b66eccbf..859090f580 100644 --- a/src/vnm_mad/remotes/lib/one_firewall.rb +++ b/src/vnm_mad/remotes/lib/one_firewall.rb @@ -95,7 +95,7 @@ module VNMMAD vm_id = @vm['ID'] process do |nic| chain = "one-#{vm_id}-#{nic[:network_id]}" - iptables_out = `#{COMMANDS[:iptables]} -n -v --line-numbers -L FORWARD` + iptables_out = `#{VNMMAD::COMMANDS[:iptables]} -n -v --line-numbers -L FORWARD` if m = iptables_out.match(/.*#{chain}.*/) rule_num = m[0].split(/\s+/)[0] purge_chain(chain, rule_num) @@ -108,7 +108,7 @@ module VNMMAD ######################################################################## # Methods to deal with iptables rules ######################################################################## - private: + private def purge_chain(chain, rule_num) rules = Array.new @@ -161,7 +161,7 @@ module VNMMAD end def tap_to_chain(tap, chain) - iptables_out = `#{COMMANDS[:iptables]} -n -v --line-numbers -L FORWARD` + iptables_out = `#{VNMMAD::COMMANDS[:iptables]} -n -v --line-numbers -L FORWARD` # Insert the rule on top of the 'opennebula' chain if it exists, so it # doesn't conflict with the security groups driver @@ -186,13 +186,13 @@ module VNMMAD end def chain_exists?(chain) - iptables_nl =`#{COMMANDS[:iptables]} -nL` + iptables_nl =`#{VNMMAD::COMMANDS[:iptables]} -nL` chains = iptables_nl.scan(/(one-.*?) .*references/).flatten chains.include? chain end def rule(rule) - "#{COMMANDS[:iptables]} #{rule}" + "#{VNMMAD::COMMANDS[:iptables]} #{rule}" end end end \ No newline at end of file diff --git a/src/vnm_mad/remotes/lib/opennebula_network.rb b/src/vnm_mad/remotes/lib/opennebula_network.rb index d1a3bd4c3e..ac2baba805 100644 --- a/src/vnm_mad/remotes/lib/opennebula_network.rb +++ b/src/vnm_mad/remotes/lib/opennebula_network.rb @@ -14,39 +14,6 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -$: << File.dirname(__FILE__) -$: << File.join(File.dirname(__FILE__), '..') - -require 'rexml/document' -require 'base64' -require 'yaml' - -require 'one_firewall' -require 'one_sg' -require 'vm' -require 'nic' -require 'address' -require 'security_groups' -require 'security_groups_iptables' - -require 'scripts_common' - -include OpenNebula - -begin - CONF = YAML.load_file( - File.join(File.dirname(__FILE__), "OpenNebulaNetwork.conf") - ) -rescue - CONF = { - :start_vlan => 2 - } -end - -# Set PATH -ENV['PATH'] = "#{ENV['PATH']}:/bin:/sbin:/usr/bin" - - ################################################################################ # The VNMMAD module provides the basic abstraction to implement custom # virtual network drivers. The VNMAD module includes: @@ -76,8 +43,8 @@ module VNMMAD @hypervisor = hypervisor end - @vm = VM.new(REXML::Document.new(vm_tpl).root, xpath_filter, - deploy_id, @hypervisor) + @vm = VNMNetwork::VM.new(REXML::Document.new(vm_tpl).root, + xpath_filter, deploy_id, @hypervisor) end # Creates a new OpenNebulaNetwork using: diff --git a/src/vnm_mad/remotes/lib/security_groups.rb b/src/vnm_mad/remotes/lib/security_groups.rb index 34a82f0cd5..494b2b1b46 100644 --- a/src/vnm_mad/remotes/lib/security_groups.rb +++ b/src/vnm_mad/remotes/lib/security_groups.rb @@ -46,14 +46,14 @@ module VNMNetwork ############################################################################ class Rule # Rule type. - TYPES = { + TYPES = [ :protocol, # Type 1: block the whole protocol :portrange, # Type 2a: block a port range within a protocol :icmp_type, # Type 2b: block selected icmp types :net, # Type 3: block a whole protocol for a network :net_portrange, # Type 4a: block a port range from a network :net_icmp_type # Type 4b: block selected icmp types from a network - } + ] # Initialize a new rule. def initialize(rule) @@ -92,7 +92,6 @@ module VNMNetwork when :net_icmp_type process_net_icmp_type(cmds, vars) - end end end @@ -114,7 +113,7 @@ module VNMNetwork end end - private: + private # ICMP Codes for each ICMP type ICMP_TYPES_EXPANDED = { @@ -140,7 +139,6 @@ module VNMNetwork return :icmp_type if !@icmp_type.nil? return :portrange if !@range.nil? return :protocol - end else return :net_icmp_type if !@icmp_type.nil? return :net_portrange if !@range.nil? diff --git a/src/vnm_mad/remotes/lib/security_groups_iptables.rb b/src/vnm_mad/remotes/lib/security_groups_iptables.rb index 86ec08d0b2..c709eba104 100644 --- a/src/vnm_mad/remotes/lib/security_groups_iptables.rb +++ b/src/vnm_mad/remotes/lib/security_groups_iptables.rb @@ -26,14 +26,14 @@ module SGIPTables ######################################################################## # Implementation of each rule type ######################################################################## - private: + private # Implements the :protocol rule. Example: # iptables -A one-3-0-i -p tcp -j RETURN def process_protocol(cmds, vars) chain = @rule_type == :inbound ? vars[:chain_in] : vars[:chain_out] - cmds << :iptables "-A #{chain} -p #{@protocol} -j RETURN" + cmds.add :iptables, "-A #{chain} -p #{@protocol} -j RETURN" end # Implements the :portrange rule. Example: @@ -41,7 +41,7 @@ module SGIPTables def process_portrange(cmds, vars) chain = @rule_type == :inbound ? vars[:chain_in] : vars[:chain_out] - cmds << :iptables "-A #{chain} -p #{@protocol} -m multiport" \ + cmds.add :iptables, "-A #{chain} -p #{@protocol} -m multiport" \ " --dports #{@range} -j RETURN" end @@ -50,7 +50,7 @@ module SGIPTables def process_icmp_type(cmds, vars) chain = @rule_type == :inbound ? vars[:chain_in] : vars[:chain_out] - cmds << :iptables "-A #{chain} -p icmp --icmp-type #{@icmp_type}" \ + cmds.add :iptables, "-A #{chain} -p icmp --icmp-type #{@icmp_type}" \ " -j RETURN" end @@ -64,17 +64,17 @@ module SGIPTables set = "#{vars[:set_sg_in]}-#{@protocol}-n" dir = "src" else - chain = : vars[:chain_out] + chain = vars[:chain_out] set = "#{vars[:set_sg_out]}-#{@protocol}-n" dir = "dst" end - cmds << :ipset "create #{set} hash:net" - cmds << :iptables "-A #{chain} -p #{@protocol} -m set" \ + cmds.add :ipset, "create #{set} hash:net" + cmds.add :iptables, "-A #{chain} -p #{@protocol} -m set" \ " --match-set #{set} #{dir} -j RETURN" net.each do |n| - cmds << :ipset "add -exist #{set} #{n}" + cmds.add :ipset, "add -exist #{set} #{n}" end end @@ -88,20 +88,20 @@ module SGIPTables set = "#{vars[:set_sg_in]}-nr" dir = "src,dst" else - chain = : vars[:chain_out] + chain = vars[:chain_out] set = "#{vars[:set_sg_out]}-nr" dir = "dst,dst" end - cmds << :ipset "create #{set} hash:net,port" - cmds << :iptables "-A #{chain} -m set --match-set" \ + cmds.add :ipset, "create #{set} hash:net,port" + cmds.add :iptables, "-A #{chain} -m set --match-set" \ "#{set} #{dir} -j RETURN" net.each do |n| @range.split(",").each do |r| r.gsub!(":","-") net_range = "#{n},#{@protocol}:#{r}" - cmds << :ipset "add -exist #{set} #{net_range}" + cmds.add :ipset, "add -exist #{set} #{net_range}" end end end @@ -116,17 +116,17 @@ module SGIPTables set = "#{vars[:set_sg_in]}-ni" dir = "src,dst" else - chain = : vars[:chain_out] + chain = vars[:chain_out] set = "#{vars[:set_sg_out]}-ni" dir = "dst,dst" end - cmds << :ipset "create #{set} hash:net,port" - cmds << :iptables "-A #{chain} -m set --match-set #{set} #{dir} -j RETURN" + cmds.add :ipset, "create #{set} hash:net,port" + cmds.add :iptables, "-A #{chain} -m set --match-set #{set} #{dir} -j RETURN" net.each do |n| icmp_type_expand.each do |type_code| - cmds << :ipset "add -exist #{set} #{n},icmp:#{type_code}" + cmds.add :ipset, "add -exist #{set} #{n},icmp:#{type_code}" end if rule.icmp_type_expand end end @@ -159,17 +159,17 @@ module SGIPTables def self.info commands = VNMNetwork::Commands.new - commands << :iptables "-S" + commands.add :iptables, "-S" iptables_s = commands.run! iptables_forwards = "" if iptables_s.match(/^-N #{GLOBAL_CHAIN}$/) - commands.iptables("-L #{GLOBAL_CHAIN} --line-numbers") + commands.add :iptables, "-L #{GLOBAL_CHAIN} --line-numbers" iptables_forwards = commands.run! end - commands << :ipset "list -name" + commands.add :ipset, "list -name" ipset_list = commands.run! { @@ -190,9 +190,9 @@ module SGIPTables commands = VNMNetwork::Commands.new - commands.iptables "-N #{GLOBAL_CHAIN}" - commands.iptables "-A FORWARD -m physdev --physdev-is-bridged -j #{GLOBAL_CHAIN}" - commands.iptables "-A #{GLOBAL_CHAIN} -j ACCEPT" + commands.add :iptables, "-N #{GLOBAL_CHAIN}" + commands.add :iptables, "-A FORWARD -m physdev --physdev-is-bridged -j #{GLOBAL_CHAIN}" + commands.add :iptables, "-A #{GLOBAL_CHAIN} -j ACCEPT" commands.run! end @@ -253,26 +253,26 @@ module SGIPTables chain_out = vars[:chain_out] # create chains - commands << :iptables "-N #{chain_in}" # inbound - commands << :iptables "-N #{chain_out}" # outbound + commands.add :iptables, "-N #{chain_in}" # inbound + commands.add :iptables, "-N #{chain_out}" # outbound # Send traffic to the NIC chains - commands << :iptables "-I #{GLOBAL_CHAIN} -m physdev --physdev-out #{nic[:tap]} --physdev-is-bridged -j #{chain_in}" - commands << :iptables "-I #{GLOBAL_CHAIN} -m physdev --physdev-in #{nic[:tap]} --physdev-is-bridged -j #{chain_out}" + commands.add :iptables, "-I #{GLOBAL_CHAIN} -m physdev --physdev-out #{nic[:tap]} --physdev-is-bridged -j #{chain_in}" + commands.add :iptables, "-I #{GLOBAL_CHAIN} -m physdev --physdev-in #{nic[:tap]} --physdev-is-bridged -j #{chain_out}" # Mac-spofing if nic[:filter_mac_spoofing] == "YES" - commands << :iptables "-A #{chain_out} -m mac ! --mac-source #{nic[:mac]} -j DROP" + commands.add :iptables, "-A #{chain_out} -m mac ! --mac-source #{nic[:mac]} -j DROP" end # IP-spofing if nic[:filter_ip_spoofing] == "YES" - commands << :iptables "-A #{chain_out} ! --source #{nic[:ip]} -j DROP" + commands.add :iptables, "-A #{chain_out} ! --source #{nic[:ip]} -j DROP" end # Related, Established - commands << :iptables "-A #{chain_in} -m state --state ESTABLISHED,RELATED -j ACCEPT" - commands << :iptables "-A #{chain_out} -m state --state ESTABLISHED,RELATED -j ACCEPT" + commands.add :iptables, "-A #{chain_in} -m state --state ESTABLISHED,RELATED -j ACCEPT" + commands.add :iptables, "-A #{chain_out} -m state --state ESTABLISHED,RELATED -j ACCEPT" commands.run! end @@ -286,8 +286,8 @@ module SGIPTables chain_out = vars[:chain_out] commands = VNMNetwork::Commands.new - commands << :iptables "-A #{chain_in} -j DROP" - commands << :iptables "-A #{chain_out} -j DROP" + commands.add :iptables, "-A #{chain_in} -j DROP" + commands.add :iptables, "-A #{chain_out} -j DROP" commands.run! end @@ -310,7 +310,7 @@ module SGIPTables fields = line.split if [chain_in, chain_out].include?(fields[1]) n = fields[0] - commands << :iptables "-D #{GLOBAL_CHAIN} #{n}" + commands.add :iptables, "-D #{GLOBAL_CHAIN} #{n}" end end @@ -320,13 +320,13 @@ module SGIPTables remove_chains << line.split[1] end end - remove_chains.each {|c| commands << :iptables "-F #{c}" } - remove_chains.each {|c| commands << :iptables "-X #{c}" } + remove_chains.each {|c| commands.add :iptables, "-F #{c}" } + remove_chains.each {|c| commands.add :iptables, "-X #{c}" } ipset_list.lines.each do |line| if line.match(/^#{chain}/) set = line.strip - commands << :ipset "destroy #{set}" + commands.add :ipset, "destroy #{set}" end end diff --git a/src/vnm_mad/remotes/lib/vm.rb b/src/vnm_mad/remotes/lib/vm.rb index 8dfa9f5f18..a09511389d 100644 --- a/src/vnm_mad/remotes/lib/vm.rb +++ b/src/vnm_mad/remotes/lib/vm.rb @@ -77,7 +77,7 @@ module VNMNetwork nil end - private: + private # Method to build the associated Hash from a NIC # @param nic_element [REXML] for the NIC diff --git a/src/vnm_mad/remotes/lib/vnmmad.rb b/src/vnm_mad/remotes/lib/vnmmad.rb new file mode 100644 index 0000000000..7698c2adb3 --- /dev/null +++ b/src/vnm_mad/remotes/lib/vnmmad.rb @@ -0,0 +1,49 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +$: << File.dirname(__FILE__) +$: << File.join(File.dirname(__FILE__), '..') + +require 'rexml/document' +require 'base64' +require 'yaml' + +require 'command' +require 'vm' +require 'nic' +require 'address' +require 'security_groups' +require 'security_groups_iptables' +require 'opennebula_network' +require 'one_firewall' +require 'one_sg' + +require 'scripts_common' + +include OpenNebula + +begin + CONF = YAML.load_file( + File.join(File.dirname(__FILE__), "OpenNebulaNetwork.conf") + ) +rescue + CONF = { + :start_vlan => 2 + } +end + +# Set PATH +ENV['PATH'] = "#{ENV['PATH']}:/bin:/sbin:/usr/bin" From e3a71b17dadbccafaffed28cd6abb2663370df06 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Mon, 22 Dec 2014 20:10:53 +0100 Subject: [PATCH 06/14] feature #3175: Move VNM drivers to new modules --- src/vnm_mad/remotes/ebtables/Ebtables.rb | 4 ++-- src/vnm_mad/remotes/ebtables/clean | 3 +-- src/vnm_mad/remotes/ebtables/post | 3 +-- src/vnm_mad/remotes/fw/clean | 4 ++-- src/vnm_mad/remotes/fw/post | 4 ++-- src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb | 16 ++++++++-------- src/vnm_mad/remotes/vmware/VMware.rb | 4 ++-- 7 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/vnm_mad/remotes/ebtables/Ebtables.rb b/src/vnm_mad/remotes/ebtables/Ebtables.rb index 4e48fd38ff..b6e93991f1 100644 --- a/src/vnm_mad/remotes/ebtables/Ebtables.rb +++ b/src/vnm_mad/remotes/ebtables/Ebtables.rb @@ -14,9 +14,9 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -require 'OpenNebulaNetwork' +require 'vnmmad' -class EbtablesVLAN < OpenNebulaNetwork +class EbtablesVLAN < VNMMAD::OpenNebulaNetwork DRIVER = "ebtables" XPATH_FILTER = "TEMPLATE/NIC[VLAN='YES']" diff --git a/src/vnm_mad/remotes/ebtables/clean b/src/vnm_mad/remotes/ebtables/clean index 2e462e6d94..c16d92259c 100755 --- a/src/vnm_mad/remotes/ebtables/clean +++ b/src/vnm_mad/remotes/ebtables/clean @@ -19,7 +19,6 @@ $: << File.dirname(__FILE__) $: << File.join(File.dirname(__FILE__), "..") -require 'OpenNebulaNetwork' require 'Ebtables' template64 = ARGV[0] @@ -27,5 +26,5 @@ template64 = ARGV[0] onevlan = EbtablesVLAN.from_base64(template64) onevlan.deactivate -filter_driver = OpenNebulaNetwork.filter_driver(template64) +filter_driver = VNMMAD::OpenNebulaNetwork.filter_driver(template64) filter_driver.deactivate diff --git a/src/vnm_mad/remotes/ebtables/post b/src/vnm_mad/remotes/ebtables/post index 88cada3ec7..c89f5ef266 100755 --- a/src/vnm_mad/remotes/ebtables/post +++ b/src/vnm_mad/remotes/ebtables/post @@ -19,7 +19,6 @@ $: << File.dirname(__FILE__) $: << File.join(File.dirname(__FILE__), "..") -require 'OpenNebulaNetwork' require 'Ebtables' template64 = ARGV[0] @@ -29,7 +28,7 @@ onevlan = EbtablesVLAN.from_base64(template64, deploy_id) onevlan.activate begin - filter_driver = OpenNebulaNetwork.filter_driver(template64, deploy_id) + filter_driver = VNMMAD::OpenNebulaNetwork.filter_driver(template64, deploy_id) filter_driver.activate rescue Exception => e OpenNebula.log_error(e.message) diff --git a/src/vnm_mad/remotes/fw/clean b/src/vnm_mad/remotes/fw/clean index ea753aa57d..b672d71ac4 100755 --- a/src/vnm_mad/remotes/fw/clean +++ b/src/vnm_mad/remotes/fw/clean @@ -19,12 +19,12 @@ $: << File.dirname(__FILE__) $: << File.join(File.dirname(__FILE__), "..") -require 'OpenNebulaNetwork' +require 'vnmmad' template64 = ARGV[0] begin - filter_driver = OpenNebulaNetwork.filter_driver(template64) + filter_driver = VNMMAD::OpenNebulaNetwork.filter_driver(template64) filter_driver.deactivate rescue Exception => e OpenNebula.log_error(e.message) diff --git a/src/vnm_mad/remotes/fw/post b/src/vnm_mad/remotes/fw/post index 76ec74671c..92a4b4518e 100755 --- a/src/vnm_mad/remotes/fw/post +++ b/src/vnm_mad/remotes/fw/post @@ -19,13 +19,13 @@ $: << File.dirname(__FILE__) $: << File.join(File.dirname(__FILE__), "..") -require 'OpenNebulaNetwork' +require 'vnmmad' template64 = ARGV[0] deploy_id = ARGV[1] begin - filter_driver = OpenNebulaNetwork.filter_driver(template64, deploy_id) + filter_driver = VNMMAD::OpenNebulaNetwork.filter_driver(template64, deploy_id) filter_driver.activate rescue Exception => e OpenNebula.log_error(e.message) diff --git a/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb b/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb index cec5fe6c38..6389f32e01 100644 --- a/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb +++ b/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb @@ -14,9 +14,9 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -require 'OpenNebulaNetwork' +require 'vnmmad' -class OpenvSwitchVLAN < OpenNebulaNetwork +class OpenvSwitchVLAN < VNMMAD::OpenNebulaNetwork DRIVER = "ovswitch" FIREWALL_PARAMS = [:black_ports_tcp, @@ -91,7 +91,7 @@ class OpenvSwitchVLAN < OpenNebulaNetwork end def tag_vlan - cmd = "#{COMMANDS[:ovs_vsctl]} set Port #{@nic[:tap]} " + cmd = "#{VNMMAD::COMMANDS[:ovs_vsctl]} set Port #{@nic[:tap]} " cmd << "tag=#{vlan}" run cmd @@ -100,7 +100,7 @@ class OpenvSwitchVLAN < OpenNebulaNetwork def tag_trunk_vlans range = @nic[:vlan_tagged_id] if range? range - ovs_vsctl_cmd = "#{COMMANDS[:ovs_vsctl]} set Port #{@nic[:tap]}" + ovs_vsctl_cmd = "#{VNMMAD::COMMANDS[:ovs_vsctl]} set Port #{@nic[:tap]}" cmd = "#{ovs_vsctl_cmd} trunks=#{range}" run cmd @@ -159,7 +159,7 @@ class OpenvSwitchVLAN < OpenNebulaNetwork def del_flows in_port = "" - dump_flows = "#{COMMANDS[:ovs_ofctl]} dump-flows #{@nic[:bridge]}" + dump_flows = "#{VNMMAD::COMMANDS[:ovs_ofctl]} dump-flows #{@nic[:bridge]}" `#{dump_flows}`.lines do |flow| next unless flow.match("#{@nic[:mac]}") flow = flow.split.select{|e| e.match(@nic[:mac])}.first @@ -175,13 +175,13 @@ class OpenvSwitchVLAN < OpenNebulaNetwork def add_flow(filter,action,priority=nil) priority = (priority.to_s.empty? ? "" : "priority=#{priority},") - run "#{COMMANDS[:ovs_ofctl]} add-flow " << + run "#{VNMMAD::COMMANDS[:ovs_ofctl]} add-flow " << "#{@nic[:bridge]} #{filter},#{priority}actions=#{action}" end def del_flow(filter) filter.gsub!(/priority=(\d+)/,"") - run "#{COMMANDS[:ovs_ofctl]} del-flows " << + run "#{VNMMAD::COMMANDS[:ovs_ofctl]} del-flows " << "#{@nic[:bridge]} #{filter}" end @@ -192,7 +192,7 @@ class OpenvSwitchVLAN < OpenNebulaNetwork def port return @nic[:port] if @nic[:port] - dump_ports = `#{COMMANDS[:ovs_ofctl]} \ + dump_ports = `#{VNMMAD::COMMANDS[:ovs_ofctl]} \ dump-ports #{@nic[:bridge]} #{@nic[:tap]}` @nic[:port] = dump_ports.scan(/^\s*port\s*(\d+):/).flatten.first diff --git a/src/vnm_mad/remotes/vmware/VMware.rb b/src/vnm_mad/remotes/vmware/VMware.rb index 53d38f3e76..446f41ad05 100644 --- a/src/vnm_mad/remotes/vmware/VMware.rb +++ b/src/vnm_mad/remotes/vmware/VMware.rb @@ -34,9 +34,9 @@ $: << RUBY_LIB_LOCATION require 'yaml' require 'CommandManager' -require 'OpenNebulaNetwork' +require 'vnmmad' -class OpenNebulaVMware < OpenNebulaNetwork +class OpenNebulaVMware < VNMMAD::OpenNebulaNetwork DRIVER = "vmware" XPATH_FILTER = "TEMPLATE/NIC" From f85a67b0dac6894aba4dcd805ffe1d12b9da17bb Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Mon, 22 Dec 2014 23:39:56 +0100 Subject: [PATCH 07/14] feature #3175: Fix bugs --- src/vnm_mad/remotes/lib/command.rb | 2 +- src/vnm_mad/remotes/lib/one_sg.rb | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vnm_mad/remotes/lib/command.rb b/src/vnm_mad/remotes/lib/command.rb index d1e65a3de7..aae4294984 100644 --- a/src/vnm_mad/remotes/lib/command.rb +++ b/src/vnm_mad/remotes/lib/command.rb @@ -62,7 +62,7 @@ module VNMNetwork cmd_str = "#{cmd} #{args.join(' ')}" end - super cmd_str + self << cmd_str end # Executes the commands array diff --git a/src/vnm_mad/remotes/lib/one_sg.rb b/src/vnm_mad/remotes/lib/one_sg.rb index ab698c6920..234d7c11d5 100644 --- a/src/vnm_mad/remotes/lib/one_sg.rb +++ b/src/vnm_mad/remotes/lib/one_sg.rb @@ -29,7 +29,7 @@ module VNMMAD def initialize(vm, deploy_id = nil, hypervisor = nil) super(vm, XPATH_FILTER, deploy_id, hypervisor) @locking = true - @commands = Commands.new + @commands = VNMMAD::VNMNetwork::Commands.new rules = {} @vm.vm_root.elements.each('TEMPLATE/SECURITY_GROUP_RULE') do |r| @@ -62,14 +62,13 @@ module VNMMAD && nic[:filter_mac_spoofing] != "YES" \ && nic[:filter_ip_spoofing] != "YES" - VNMMAD::SGIPTables.nic_pre(@vm, nic) sg_ids = nic[:security_groups].split(",") sg_ids.each do |sg_id| rules = @security_group_rules[sg_id] - + sg = VNMMAD::SGIPTables::SecurityGroupIPTables.new(@vm, nic, sg_id, rules) From f0e9705f46518a010f17d29e779138aada0dfcdb Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Tue, 23 Dec 2014 00:09:45 +0100 Subject: [PATCH 08/14] feature #3175: Fix minor bugs. Add simple test --- src/vnm_mad/remotes/lib/address.rb | 2 +- src/vnm_mad/remotes/lib/security_groups.rb | 2 +- .../remotes/lib/security_groups_iptables.rb | 8 +- src/vnm_mad/remotes/test/sg_test_pre.rb | 146 ++++++++++++++++++ 4 files changed, 154 insertions(+), 4 deletions(-) create mode 100755 src/vnm_mad/remotes/test/sg_test_pre.rb diff --git a/src/vnm_mad/remotes/lib/address.rb b/src/vnm_mad/remotes/lib/address.rb index f32838b7d4..c934126bf7 100644 --- a/src/vnm_mad/remotes/lib/address.rb +++ b/src/vnm_mad/remotes/lib/address.rb @@ -21,7 +21,7 @@ module VNMNetwork # This methods translates an address range to a set of IPv4 networks # in CIDR notation # @param ip_start [String] First IP of the range in dot notation - # @param size [String] The number of IPs in the range + # @param size [Fixnum] The number of IPs in the range # # @return [Array] The networks in CIDR def self.to_nets(ip_start, size) diff --git a/src/vnm_mad/remotes/lib/security_groups.rb b/src/vnm_mad/remotes/lib/security_groups.rb index 494b2b1b46..e8e3b92539 100644 --- a/src/vnm_mad/remotes/lib/security_groups.rb +++ b/src/vnm_mad/remotes/lib/security_groups.rb @@ -100,7 +100,7 @@ module VNMNetwork def net return [] if @ip.nil? || @size.nil? - VNMNetwork::to_nets(@ip, @size) + VNMNetwork::to_nets(@ip, @size.to_i) end # Expand the ICMP type with associated codes if any diff --git a/src/vnm_mad/remotes/lib/security_groups_iptables.rb b/src/vnm_mad/remotes/lib/security_groups_iptables.rb index c709eba104..7ad36e0282 100644 --- a/src/vnm_mad/remotes/lib/security_groups_iptables.rb +++ b/src/vnm_mad/remotes/lib/security_groups_iptables.rb @@ -111,7 +111,7 @@ module SGIPTables # iptables -A one-3-0-i -m set --match-set one-3-0-1-i-nr src,dst -j RETURN # ipset add -exist one-3-0-1-i-ni 10.0.0.0/24,icmp:8/0 def process_net_icmp_type(cmds, vars) - if rule.rule_type == :inbound + if @rule_type == :inbound chain = vars[:chain_in] set = "#{vars[:set_sg_in]}-ni" dir = "src,dst" @@ -127,7 +127,7 @@ module SGIPTables net.each do |n| icmp_type_expand.each do |type_code| cmds.add :ipset, "add -exist #{set} #{n},icmp:#{type_code}" - end if rule.icmp_type_expand + end end end end @@ -142,6 +142,10 @@ module SGIPTables @vars = SGIPTables.vars(@vm, @nic, @sg_id) end + + def new_rule(rule) + RuleIPTables.new(rule) + end end ############################################################################ diff --git a/src/vnm_mad/remotes/test/sg_test_pre.rb b/src/vnm_mad/remotes/test/sg_test_pre.rb new file mode 100755 index 0000000000..49a0a73160 --- /dev/null +++ b/src/vnm_mad/remotes/test/sg_test_pre.rb @@ -0,0 +1,146 @@ +#!/usr/bin/env ruby + +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +$: << File.dirname(__FILE__) + '/..' +$: << File.dirname(__FILE__) + '/../lib' +$: << File.dirname(__FILE__) + '/../../../mad/ruby' + +require 'vnmmad' + +module VNMMAD +module VNMNetwork + class Nics < Array + def initialize(hypervisor) + @nicClass = NicTest + end + end + + class NicTest < Hash + def initialize + super(nil) + end + + def get_info(vm) + end + + def get_tap(vm) + self[:tap] = "vnet0" + self + end + end + + class Commands < Array + def run! + self.each{ |c| puts "#{c}"} + clear + return "" + end + end +end +end + +vm_xml=< + 3 + + +EOF + +one_sg = VNMMAD::OpenNebulaSG.new(vm_xml, "one-0", "test") +one_sg.activate \ No newline at end of file From 354cd84a2a867384fc01fd29e033fa2d5ab68a35 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Tue, 23 Dec 2014 00:17:07 +0100 Subject: [PATCH 09/14] feature #3175: Fix bug --- src/vnm_mad/remotes/lib/security_groups_iptables.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vnm_mad/remotes/lib/security_groups_iptables.rb b/src/vnm_mad/remotes/lib/security_groups_iptables.rb index 7ad36e0282..2c9bf3027a 100644 --- a/src/vnm_mad/remotes/lib/security_groups_iptables.rb +++ b/src/vnm_mad/remotes/lib/security_groups_iptables.rb @@ -95,7 +95,7 @@ module SGIPTables cmds.add :ipset, "create #{set} hash:net,port" cmds.add :iptables, "-A #{chain} -m set --match-set" \ - "#{set} #{dir} -j RETURN" + " #{set} #{dir} -j RETURN" net.each do |n| @range.split(",").each do |r| From 0961dc48297bd0173f3042f5df9b51440b2dca77 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Wed, 24 Dec 2014 00:39:41 +0100 Subject: [PATCH 10/14] feature #3175: Rename OpenNebulaNetwork class to VNMDriver. Change filenames also --- install.sh | 6 ++-- src/vnm_mad/remotes/802.1Q/HostManaged.rb | 2 +- src/vnm_mad/remotes/ebtables/Ebtables.rb | 2 +- src/vnm_mad/remotes/ebtables/clean | 2 +- src/vnm_mad/remotes/ebtables/post | 2 +- src/vnm_mad/remotes/fw/clean | 2 +- src/vnm_mad/remotes/fw/post | 2 +- .../lib/{one_firewall.rb => fw_driver.rb} | 5 ++-- .../remotes/lib/{one_sg.rb => sg_driver.rb} | 29 ++++++++++--------- .../{opennebula_network.rb => vnm_driver.rb} | 18 ++++++------ src/vnm_mad/remotes/lib/vnmmad.rb | 6 ++-- src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb | 2 +- src/vnm_mad/remotes/test/sg_test_pre.rb | 4 +-- src/vnm_mad/remotes/vmware/VMware.rb | 2 +- 14 files changed, 43 insertions(+), 41 deletions(-) rename src/vnm_mad/remotes/lib/{one_firewall.rb => fw_driver.rb} (98%) rename src/vnm_mad/remotes/lib/{one_sg.rb => sg_driver.rb} (82%) rename src/vnm_mad/remotes/lib/{opennebula_network.rb => vnm_driver.rb} (91%) diff --git a/install.sh b/install.sh index 3412a04599..60736ec669 100755 --- a/install.sh +++ b/install.sh @@ -932,11 +932,11 @@ AUTH_PLAIN_FILES="src/authm_mad/remotes/plain/authenticate" # Virtual Network Manager drivers to be installed under $REMOTES_LOCATION/vnm #------------------------------------------------------------------------------- -NETWORK_FILES="src/vnm_mad/remotes/lib/opennebula_network.rb \ +NETWORK_FILES="src/vnm_mad/remotes/lib/vnm_driver.rb \ src/vnm_mad/remotes/lib/vnmmad.rb \ src/vnm_mad/remotes/OpenNebulaNetwork.conf \ - src/vnm_mad/remotes/lib/one_firewall.rb \ - src/vnm_mad/remotes/lib/one_sg.rb \ + src/vnm_mad/remotes/lib/fw_driver.rb \ + src/vnm_mad/remotes/lib/sg_driver.rb \ src/vnm_mad/remotes/lib/address.rb \ src/vnm_mad/remotes/lib/command.rb \ src/vnm_mad/remotes/lib/vm.rb \ diff --git a/src/vnm_mad/remotes/802.1Q/HostManaged.rb b/src/vnm_mad/remotes/802.1Q/HostManaged.rb index 88bd38af1f..f33ce912a8 100644 --- a/src/vnm_mad/remotes/802.1Q/HostManaged.rb +++ b/src/vnm_mad/remotes/802.1Q/HostManaged.rb @@ -16,7 +16,7 @@ require 'vnmmad' -class OpenNebulaHM < VNMMAD::OpenNebulaNetwork +class OpenNebulaHM < VNMMAD::VNMDriver DRIVER = "802.1Q" XPATH_FILTER = "TEMPLATE/NIC[VLAN='YES']" diff --git a/src/vnm_mad/remotes/ebtables/Ebtables.rb b/src/vnm_mad/remotes/ebtables/Ebtables.rb index b6e93991f1..bae81aabd9 100644 --- a/src/vnm_mad/remotes/ebtables/Ebtables.rb +++ b/src/vnm_mad/remotes/ebtables/Ebtables.rb @@ -16,7 +16,7 @@ require 'vnmmad' -class EbtablesVLAN < VNMMAD::OpenNebulaNetwork +class EbtablesVLAN < VNMMAD::VNMDriver DRIVER = "ebtables" XPATH_FILTER = "TEMPLATE/NIC[VLAN='YES']" diff --git a/src/vnm_mad/remotes/ebtables/clean b/src/vnm_mad/remotes/ebtables/clean index c16d92259c..e552268bc9 100755 --- a/src/vnm_mad/remotes/ebtables/clean +++ b/src/vnm_mad/remotes/ebtables/clean @@ -26,5 +26,5 @@ template64 = ARGV[0] onevlan = EbtablesVLAN.from_base64(template64) onevlan.deactivate -filter_driver = VNMMAD::OpenNebulaNetwork.filter_driver(template64) +filter_driver = VNMMAD::VNMDriver.filter_driver(template64) filter_driver.deactivate diff --git a/src/vnm_mad/remotes/ebtables/post b/src/vnm_mad/remotes/ebtables/post index c89f5ef266..7199b51231 100755 --- a/src/vnm_mad/remotes/ebtables/post +++ b/src/vnm_mad/remotes/ebtables/post @@ -28,7 +28,7 @@ onevlan = EbtablesVLAN.from_base64(template64, deploy_id) onevlan.activate begin - filter_driver = VNMMAD::OpenNebulaNetwork.filter_driver(template64, deploy_id) + filter_driver = VNMMAD::VNMDriver.filter_driver(template64, deploy_id) filter_driver.activate rescue Exception => e OpenNebula.log_error(e.message) diff --git a/src/vnm_mad/remotes/fw/clean b/src/vnm_mad/remotes/fw/clean index b672d71ac4..489c5296df 100755 --- a/src/vnm_mad/remotes/fw/clean +++ b/src/vnm_mad/remotes/fw/clean @@ -24,7 +24,7 @@ require 'vnmmad' template64 = ARGV[0] begin - filter_driver = VNMMAD::OpenNebulaNetwork.filter_driver(template64) + filter_driver = VNMMAD::VNMDriver.filter_driver(template64) filter_driver.deactivate rescue Exception => e OpenNebula.log_error(e.message) diff --git a/src/vnm_mad/remotes/fw/post b/src/vnm_mad/remotes/fw/post index 92a4b4518e..c8d1b74edb 100755 --- a/src/vnm_mad/remotes/fw/post +++ b/src/vnm_mad/remotes/fw/post @@ -25,7 +25,7 @@ template64 = ARGV[0] deploy_id = ARGV[1] begin - filter_driver = VNMMAD::OpenNebulaNetwork.filter_driver(template64, deploy_id) + filter_driver = VNMMAD::VNMDriver.filter_driver(template64, deploy_id) filter_driver.activate rescue Exception => e OpenNebula.log_error(e.message) diff --git a/src/vnm_mad/remotes/lib/one_firewall.rb b/src/vnm_mad/remotes/lib/fw_driver.rb similarity index 98% rename from src/vnm_mad/remotes/lib/one_firewall.rb rename to src/vnm_mad/remotes/lib/fw_driver.rb index 859090f580..6a70883384 100644 --- a/src/vnm_mad/remotes/lib/one_firewall.rb +++ b/src/vnm_mad/remotes/lib/fw_driver.rb @@ -19,7 +19,7 @@ module VNMMAD ############################################################################ # Filter network driver based on simple iptables rules ############################################################################ - class OpenNebulaFirewall < VNMMAD::OpenNebulaNetwork + class FWDriver < VNMDriver # Driver name DRIVER = "fw" @@ -36,11 +36,11 @@ module VNMMAD end # Function to activate the driver in the VM - # def activate lock vm_id = @vm['ID'] + process do |nic| #:white_ports_tcp => iptables_range #:white_ports_udp => iptables_range @@ -89,6 +89,7 @@ module VNMMAD unlock end + # Method to clean iptables chains def deactivate lock diff --git a/src/vnm_mad/remotes/lib/one_sg.rb b/src/vnm_mad/remotes/lib/sg_driver.rb similarity index 82% rename from src/vnm_mad/remotes/lib/one_sg.rb rename to src/vnm_mad/remotes/lib/sg_driver.rb index 234d7c11d5..7c7616991a 100644 --- a/src/vnm_mad/remotes/lib/one_sg.rb +++ b/src/vnm_mad/remotes/lib/sg_driver.rb @@ -14,22 +14,21 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -require 'opennebula_network' - module VNMMAD ############################################################################ # OpenNebula Firewall with Security Groups Based on IPTables (KVM and Xen) ############################################################################ - class OpenNebulaSG < VNMMAD::OpenNebulaNetwork + class SGDriver < VNMDriver - DRIVER = "sg" - XPATH_FILTER = "TEMPLATE/NIC" + DRIVER = "sg" + XPATH_FILTER = "TEMPLATE/NIC" + # Creates a new SG driver and scans SG Rules def initialize(vm, deploy_id = nil, hypervisor = nil) super(vm, XPATH_FILTER, deploy_id, hypervisor) - @locking = true - @commands = VNMMAD::VNMNetwork::Commands.new + @locking = true + @commands = VNMNetwork::Commands.new rules = {} @vm.vm_root.elements.each('TEMPLATE/SECURITY_GROUP_RULE') do |r| @@ -49,12 +48,14 @@ module VNMMAD @security_group_rules = rules end + # Activate the rules, bootstrap iptables chains and set filter rules for + # each VM NIC def activate deactivate lock # Global Bootstrap - VNMMAD::SGIPTables.global_bootstrap + SGIPTables.global_bootstrap # Process the rules @vm.nics.each do |nic| @@ -62,15 +63,15 @@ module VNMMAD && nic[:filter_mac_spoofing] != "YES" \ && nic[:filter_ip_spoofing] != "YES" - VNMMAD::SGIPTables.nic_pre(@vm, nic) + SGIPTables.nic_pre(@vm, nic) sg_ids = nic[:security_groups].split(",") sg_ids.each do |sg_id| rules = @security_group_rules[sg_id] - sg = VNMMAD::SGIPTables::SecurityGroupIPTables.new(@vm, nic, - sg_id, rules) + sg = SGIPTables::SecurityGroupIPTables.new(@vm, nic, sg_id, + rules) begin sg.process_rules @@ -82,18 +83,19 @@ module VNMMAD end end - VNMMAD::SGIPTables.nic_post(@vm, nic) + SGIPTables.nic_post(@vm, nic) end unlock end + # Clean iptables rules and chains def deactivate lock begin @vm.nics.each do |nic| - VNMMAD::SGIPTables.nic_deactivate(@vm, nic) + SGIPTables.nic_deactivate(@vm, nic) end rescue Exception => e raise e @@ -102,5 +104,4 @@ module VNMMAD end end end - end diff --git a/src/vnm_mad/remotes/lib/opennebula_network.rb b/src/vnm_mad/remotes/lib/vnm_driver.rb similarity index 91% rename from src/vnm_mad/remotes/lib/opennebula_network.rb rename to src/vnm_mad/remotes/lib/vnm_driver.rb index ac2baba805..cd2cd8712f 100644 --- a/src/vnm_mad/remotes/lib/opennebula_network.rb +++ b/src/vnm_mad/remotes/lib/vnm_driver.rb @@ -17,19 +17,19 @@ ################################################################################ # The VNMMAD module provides the basic abstraction to implement custom # virtual network drivers. The VNMAD module includes: -# - VNMNetwork with base classes and main functionality +# - VNMNetwork with base classes and main functionality to manage Virtual Nets # - SGIPTables a module with a SG implementation based in iptables/ipset ################################################################################ module VNMMAD ############################################################################ # Base driver class to implement a Network driver. It relays on two filter - # drivers OpenNebulaFirewall and OpenNebulaSG. + # drivers FirewallDriver and SGDriver. ############################################################################ - class OpenNebulaNetwork + class VNMDriver attr_reader :hypervisor, :vm - # Creates new OpenNebulaNetwork using: + # Creates new driver using: # @param vm_tpl [String] XML String from oned # @param xpath_filter [String] to get relevant NICs for the driver # @param deploy_id [String] @@ -47,7 +47,7 @@ module VNMMAD xpath_filter, deploy_id, @hypervisor) end - # Creates a new OpenNebulaNetwork using: + # Creates a new VNDriver using: # @param vm_64 [String] Base64 encoded XML String from oned # @param deploy_id [String] # @param hypervisor [String] @@ -127,19 +127,19 @@ module VNMMAD # @return Boolean def self.has_fw_attrs?(vm_xml) vm_root = REXML::Document.new(vm_xml).root - !vm_root.elements[OpenNebulaFirewall::XPATH_FILTER].nil? + !vm_root.elements[FWDriver::XPATH_FILTER].nil? end # Returns a filter object based on the contents of the template # - # @return OpenNebulaFirewall or OpenNebulaSG object + # @return FWDriver or SGDriver object def self.filter_driver(vm_64, deploy_id = nil, hypervisor = nil) vm_xml = Base64::decode64(vm_64) if self.has_fw_attrs?(vm_xml) - OpenNebulaFirewall.new(vm_xml, deploy_id, hypervisor) + FWDriver.new(vm_xml, deploy_id, hypervisor) else - OpenNebulaSG.new(vm_xml, deploy_id, hypervisor) + SGDriver.new(vm_xml, deploy_id, hypervisor) end end end diff --git a/src/vnm_mad/remotes/lib/vnmmad.rb b/src/vnm_mad/remotes/lib/vnmmad.rb index 7698c2adb3..dad4386931 100644 --- a/src/vnm_mad/remotes/lib/vnmmad.rb +++ b/src/vnm_mad/remotes/lib/vnmmad.rb @@ -27,9 +27,9 @@ require 'nic' require 'address' require 'security_groups' require 'security_groups_iptables' -require 'opennebula_network' -require 'one_firewall' -require 'one_sg' +require 'vnm_driver' +require 'fw_driver' +require 'sg_driver' require 'scripts_common' diff --git a/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb b/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb index 6389f32e01..91e0c94233 100644 --- a/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb +++ b/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb @@ -16,7 +16,7 @@ require 'vnmmad' -class OpenvSwitchVLAN < VNMMAD::OpenNebulaNetwork +class OpenvSwitchVLAN < VNMMAD::VNMDriver DRIVER = "ovswitch" FIREWALL_PARAMS = [:black_ports_tcp, diff --git a/src/vnm_mad/remotes/test/sg_test_pre.rb b/src/vnm_mad/remotes/test/sg_test_pre.rb index 49a0a73160..9d32ec9f26 100755 --- a/src/vnm_mad/remotes/test/sg_test_pre.rb +++ b/src/vnm_mad/remotes/test/sg_test_pre.rb @@ -142,5 +142,5 @@ vm_xml=< EOF -one_sg = VNMMAD::OpenNebulaSG.new(vm_xml, "one-0", "test") -one_sg.activate \ No newline at end of file +one_sg = VNMMAD::SGDriver.new(vm_xml, "one-0", "test") +one_sg.activate diff --git a/src/vnm_mad/remotes/vmware/VMware.rb b/src/vnm_mad/remotes/vmware/VMware.rb index 446f41ad05..722a502890 100644 --- a/src/vnm_mad/remotes/vmware/VMware.rb +++ b/src/vnm_mad/remotes/vmware/VMware.rb @@ -36,7 +36,7 @@ require 'yaml' require 'CommandManager' require 'vnmmad' -class OpenNebulaVMware < VNMMAD::OpenNebulaNetwork +class OpenNebulaVMware < VNMMAD::VNMDriver DRIVER = "vmware" XPATH_FILTER = "TEMPLATE/NIC" From ae0bf324005a2eb478d4a53b784f8e8f8fd5364e Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Wed, 24 Dec 2014 17:00:23 +0100 Subject: [PATCH 11/14] feature #3175b: rename 802.1Q class name to VLANDriver --- install.sh | 2 +- src/vnm_mad/remotes/802.1Q/pre | 4 +-- .../802.1Q/{HostManaged.rb => vlan_driver.rb} | 26 ++++++++++++++++--- 3 files changed, 26 insertions(+), 6 deletions(-) rename src/vnm_mad/remotes/802.1Q/{HostManaged.rb => vlan_driver.rb} (72%) diff --git a/install.sh b/install.sh index 60736ec669..6c9907c2fd 100755 --- a/install.sh +++ b/install.sh @@ -947,7 +947,7 @@ NETWORK_FILES="src/vnm_mad/remotes/lib/vnm_driver.rb \ NETWORK_8021Q_FILES="src/vnm_mad/remotes/802.1Q/clean \ src/vnm_mad/remotes/802.1Q/post \ src/vnm_mad/remotes/802.1Q/pre \ - src/vnm_mad/remotes/802.1Q/HostManaged.rb" + src/vnm_mad/remotes/802.1Q/vlan_driver.rb" NETWORK_DUMMY_FILES="src/vnm_mad/remotes/dummy/clean \ src/vnm_mad/remotes/dummy/post \ diff --git a/src/vnm_mad/remotes/802.1Q/pre b/src/vnm_mad/remotes/802.1Q/pre index a3be35cc4d..656fca6415 100755 --- a/src/vnm_mad/remotes/802.1Q/pre +++ b/src/vnm_mad/remotes/802.1Q/pre @@ -19,7 +19,7 @@ $: << File.dirname(__FILE__) $: << File.join(File.dirname(__FILE__), "..") -require 'HostManaged' +require 'vlan_driver' -hm = OpenNebulaHM.from_base64(ARGV[0]) +hm = VLANDriver.from_base64(ARGV[0]) exit hm.activate diff --git a/src/vnm_mad/remotes/802.1Q/HostManaged.rb b/src/vnm_mad/remotes/802.1Q/vlan_driver.rb similarity index 72% rename from src/vnm_mad/remotes/802.1Q/HostManaged.rb rename to src/vnm_mad/remotes/802.1Q/vlan_driver.rb index f33ce912a8..3dfbd7b949 100644 --- a/src/vnm_mad/remotes/802.1Q/HostManaged.rb +++ b/src/vnm_mad/remotes/802.1Q/vlan_driver.rb @@ -16,13 +16,24 @@ require 'vnmmad' -class OpenNebulaHM < VNMMAD::VNMDriver - DRIVER = "802.1Q" +################################################################################ +# This driver tag VM traffic with a VLAN_ID using 802.1Q protocol. Features: +# - Creates a bridge and bind phisycal device if not present +# - Creates a tagged interface for the VM dev.vlan_id +# +# Once activated the VM will be attached to this bridge +################################################################################ +class VLANDriver < VNMMAD::VNMDriver + # DRIVER name and XPATH for relevant NICs + DRIVER = "802.1Q" XPATH_FILTER = "TEMPLATE/NIC[VLAN='YES']" + ############################################################################ + # Creatges the driver device operations are not locked + ############################################################################ def initialize(vm, deploy_id = nil, hypervisor = nil) - super(vm,XPATH_FILTER,deploy_id,hypervisor) + super(vm, XPATH_FILTER, deploy_id, hypervisor) @locking = false lock @@ -30,6 +41,9 @@ class OpenNebulaHM < VNMMAD::VNMDriver unlock end + ############################################################################ + # Activate the driver and creates bridges and tags devices as needed. + ############################################################################ def activate lock @@ -67,6 +81,12 @@ class OpenNebulaHM < VNMMAD::VNMDriver return 0 end + ############################################################################ + # Private interface, methods to manage bridges and VLAN tags through the + # brctl and ip commands + ############################################################################ + private + def bridge_exists?(bridge) @bridges.keys.include? bridge end From 0fcbadea8053983f6f8e158bb71caf414f3fcfb3 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Sat, 27 Dec 2014 01:28:25 +0100 Subject: [PATCH 12/14] feature #3175: Fix bugs. New method to access commands --- src/vnm_mad/remotes/802.1Q/vlan_driver.rb | 12 +++++++----- src/vnm_mad/remotes/lib/fw_driver.rb | 8 ++++---- src/vnm_mad/remotes/lib/vnm_driver.rb | 12 ++++++++++++ src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb | 12 ++++++------ 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/vnm_mad/remotes/802.1Q/vlan_driver.rb b/src/vnm_mad/remotes/802.1Q/vlan_driver.rb index 3dfbd7b949..f3dcf5dabc 100644 --- a/src/vnm_mad/remotes/802.1Q/vlan_driver.rb +++ b/src/vnm_mad/remotes/802.1Q/vlan_driver.rb @@ -92,18 +92,18 @@ class VLANDriver < VNMMAD::VNMDriver end def create_bridge(bridge) - OpenNebula.exec_and_log("#{VNMMAD::COMMANDS[:brctl]} addbr #{bridge}") + OpenNebula.exec_and_log("#{command(:brctl)} addbr #{bridge}") @bridges[bridge] = Array.new end def device_exists?(dev, vlan=nil) dev = "#{dev}.#{vlan}" if vlan - `#{VNMMAD::COMMANDS[:ip]} link show #{dev}` + `#{command(:ip)} link show #{dev}` $?.exitstatus == 0 end def create_dev_vlan(dev, vlan) - cmd = "#{VNMMAD::COMMANDS[:ip]} link add link #{dev}" + cmd = "#{command(:ip)} link add link #{dev}" cmd << " name #{dev}.#{vlan} type vlan id #{vlan}" OpenNebula.exec_and_log(cmd) @@ -111,18 +111,20 @@ class VLANDriver < VNMMAD::VNMDriver def attached_bridge_dev?(bridge, dev, vlan=nil) return false if !bridge_exists? bridge + dev = "#{dev}.#{vlan}" if vlan @bridges[bridge].include? dev end def attach_brigde_dev(bridge, dev, vlan=nil) dev = "#{dev}.#{vlan}" if vlan - OpenNebula.exec_and_log("#{VNMMAD::COMMANDS[:brctl]} addif #{bridge} #{dev}") + + OpenNebula.exec_and_log("#{command(:brctl)} addif #{bridge} #{dev}") @bridges[bridge] << dev end def ifup(dev, vlan=nil) dev = "#{dev}.#{vlan}" if vlan - OpenNebula.exec_and_log("#{VNMMAD::COMMANDS[:ip]} link set #{dev} up") + OpenNebula.exec_and_log("#{command(:ip)} link set #{dev} up") end end diff --git a/src/vnm_mad/remotes/lib/fw_driver.rb b/src/vnm_mad/remotes/lib/fw_driver.rb index 6a70883384..892f28944e 100644 --- a/src/vnm_mad/remotes/lib/fw_driver.rb +++ b/src/vnm_mad/remotes/lib/fw_driver.rb @@ -96,7 +96,7 @@ module VNMMAD vm_id = @vm['ID'] process do |nic| chain = "one-#{vm_id}-#{nic[:network_id]}" - iptables_out = `#{VNMMAD::COMMANDS[:iptables]} -n -v --line-numbers -L FORWARD` + iptables_out = `#{command(:iptables)} -n -v --line-numbers -L FORWARD` if m = iptables_out.match(/.*#{chain}.*/) rule_num = m[0].split(/\s+/)[0] purge_chain(chain, rule_num) @@ -162,7 +162,7 @@ module VNMMAD end def tap_to_chain(tap, chain) - iptables_out = `#{VNMMAD::COMMANDS[:iptables]} -n -v --line-numbers -L FORWARD` + iptables_out = `#{command(:iptables)} -n -v --line-numbers -L FORWARD` # Insert the rule on top of the 'opennebula' chain if it exists, so it # doesn't conflict with the security groups driver @@ -187,13 +187,13 @@ module VNMMAD end def chain_exists?(chain) - iptables_nl =`#{VNMMAD::COMMANDS[:iptables]} -nL` + iptables_nl =`#{command(:iptables)} -nL` chains = iptables_nl.scan(/(one-.*?) .*references/).flatten chains.include? chain end def rule(rule) - "#{VNMMAD::COMMANDS[:iptables]} #{rule}" + "#{command(:iptables)} #{rule}" end end end \ No newline at end of file diff --git a/src/vnm_mad/remotes/lib/vnm_driver.rb b/src/vnm_mad/remotes/lib/vnm_driver.rb index cd2cd8712f..f62df56e06 100644 --- a/src/vnm_mad/remotes/lib/vnm_driver.rb +++ b/src/vnm_mad/remotes/lib/vnm_driver.rb @@ -142,5 +142,17 @@ module VNMMAD SGDriver.new(vm_xml, deploy_id, hypervisor) end end + + # Returns the associated command including sudo and other configuration + # attributes + def command(cmd) + if VNMNetwork::COMMANDS.keys.include?(cmd.to_sym) + cmd_str = "#{VNMNetwork::COMMANDS[cmd.to_sym]}" + else + cmd_str = "#{cmd}" + end + + return cmd_str + end end end diff --git a/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb b/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb index 91e0c94233..b6160ecc22 100644 --- a/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb +++ b/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb @@ -91,7 +91,7 @@ class OpenvSwitchVLAN < VNMMAD::VNMDriver end def tag_vlan - cmd = "#{VNMMAD::COMMANDS[:ovs_vsctl]} set Port #{@nic[:tap]} " + cmd = "#{command(:ovs_vsctl)} set Port #{@nic[:tap]} " cmd << "tag=#{vlan}" run cmd @@ -100,7 +100,7 @@ class OpenvSwitchVLAN < VNMMAD::VNMDriver def tag_trunk_vlans range = @nic[:vlan_tagged_id] if range? range - ovs_vsctl_cmd = "#{VNMMAD::COMMANDS[:ovs_vsctl]} set Port #{@nic[:tap]}" + ovs_vsctl_cmd = "#{command(:ovs_vsctl)} set Port #{@nic[:tap]}" cmd = "#{ovs_vsctl_cmd} trunks=#{range}" run cmd @@ -159,7 +159,7 @@ class OpenvSwitchVLAN < VNMMAD::VNMDriver def del_flows in_port = "" - dump_flows = "#{VNMMAD::COMMANDS[:ovs_ofctl]} dump-flows #{@nic[:bridge]}" + dump_flows = "#{command(:ovs_ofctl)} dump-flows #{@nic[:bridge]}" `#{dump_flows}`.lines do |flow| next unless flow.match("#{@nic[:mac]}") flow = flow.split.select{|e| e.match(@nic[:mac])}.first @@ -175,13 +175,13 @@ class OpenvSwitchVLAN < VNMMAD::VNMDriver def add_flow(filter,action,priority=nil) priority = (priority.to_s.empty? ? "" : "priority=#{priority},") - run "#{VNMMAD::COMMANDS[:ovs_ofctl]} add-flow " << + run "#{command(:ovs_ofctl)} add-flow " << "#{@nic[:bridge]} #{filter},#{priority}actions=#{action}" end def del_flow(filter) filter.gsub!(/priority=(\d+)/,"") - run "#{VNMMAD::COMMANDS[:ovs_ofctl]} del-flows " << + run "#{command(:ovs_ofctl)} del-flows " << "#{@nic[:bridge]} #{filter}" end @@ -192,7 +192,7 @@ class OpenvSwitchVLAN < VNMMAD::VNMDriver def port return @nic[:port] if @nic[:port] - dump_ports = `#{VNMMAD::COMMANDS[:ovs_ofctl]} \ + dump_ports = `#{command(:ovs_ofctl)} \ dump-ports #{@nic[:bridge]} #{@nic[:tap]}` @nic[:port] = dump_ports.scan(/^\s*port\s*(\d+):/).flatten.first From 1420166880f002f489a0c4ebca5fa62c11386c37 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Sat, 27 Dec 2014 22:21:56 +0100 Subject: [PATCH 13/14] feature #3175: Fix spoofing with non-SG VMs --- src/vnm_mad/remotes/lib/sg_driver.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vnm_mad/remotes/lib/sg_driver.rb b/src/vnm_mad/remotes/lib/sg_driver.rb index 7c7616991a..8390e7d948 100644 --- a/src/vnm_mad/remotes/lib/sg_driver.rb +++ b/src/vnm_mad/remotes/lib/sg_driver.rb @@ -59,9 +59,7 @@ module VNMMAD # Process the rules @vm.nics.each do |nic| - next if nic[:security_groups].nil? \ - && nic[:filter_mac_spoofing] != "YES" \ - && nic[:filter_ip_spoofing] != "YES" + next if nic[:security_groups].nil? SGIPTables.nic_pre(@vm, nic) From 169f5648925318e5f5e82faf812c3669ea523d91 Mon Sep 17 00:00:00 2001 From: Jaime Melis Date: Sun, 28 Dec 2014 10:40:58 +0100 Subject: [PATCH 14/14] feature #3175: Check if RANGE has proper syntax --- include/NebulaUtil.h | 9 +++++++++ src/common/NebulaUtil.cc | 23 +++++++++++++++++++++++ src/secgroup/SecurityGroup.cc | 10 ++++++++++ 3 files changed, 42 insertions(+) diff --git a/include/NebulaUtil.h b/include/NebulaUtil.h index e4319cbede..2de7d98b40 100644 --- a/include/NebulaUtil.h +++ b/include/NebulaUtil.h @@ -158,6 +158,15 @@ namespace one_util * @return */ std::string float_to_str(const float &num); + + /** + * Checks if a strings matches a regular expression + * + * @param pattern PCRE extended pattern + * @param subject the string to test + * @return 0 on match, another value otherwise + */ + int regex_match(const char *pattern, const char *subject); }; #endif /* _NEBULA_UTIL_H_ */ diff --git a/src/common/NebulaUtil.cc b/src/common/NebulaUtil.cc index 7c47e2c5b9..edd42ad6f5 100644 --- a/src/common/NebulaUtil.cc +++ b/src/common/NebulaUtil.cc @@ -28,6 +28,8 @@ #include #include #include +#include +#include using namespace std; @@ -260,3 +262,24 @@ string one_util::float_to_str(const float &num) return oss.str(); } + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int one_util::regex_match(const char *pattern, const char *subject) +{ + int rc; + regex_t re; + + rc = regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB); + + if (rc != 0) + { + return(rc); + } + + rc = regexec(&re, subject, 0, 0, 0); + regfree(&re); + + return rc; +} diff --git a/src/secgroup/SecurityGroup.cc b/src/secgroup/SecurityGroup.cc index c519484fec..814f26d8fc 100644 --- a/src/secgroup/SecurityGroup.cc +++ b/src/secgroup/SecurityGroup.cc @@ -349,6 +349,16 @@ bool SecurityGroup::isValidRule(const VectorAttribute * rule, string& error) con return false; } + if (!value.empty()) + { + const char *range_pattern = "^(([[:digit:]]+|[[:digit:]]+:[[:digit:]]+),)*([[:digit:]]+|[[:digit:]]+:[[:digit:]]+)$"; + if (one_util::regex_match(range_pattern, value.c_str()) != 0) + { + error = "Invalid RANGE specification."; + return false; + } + } + value = rule->vector_value("ICMP_TYPE"); if (!value.empty())