mirror of
https://github.com/OpenNebula/one.git
synced 2025-01-11 05:17:41 +03:00
feature #3175: Moved firewall and SG drivers to new module. Removed old files
This commit is contained in:
parent
fba4deec6e
commit
536be6ecf0
14
install.sh
14
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 \
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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 '<TYPE>' or '<TYPE>/<CODE>', where both
|
||||
# '<TYPE>' and '<CODE>' are integers. This class has a helper method
|
||||
# tgat expands '<TYPE>' into all the '<TYPE>/<CODE>' 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
|
198
src/vnm_mad/remotes/lib/one_firewall.rb
Normal file
198
src/vnm_mad/remotes/lib/one_firewall.rb
Normal file
@ -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
|
107
src/vnm_mad/remotes/lib/one_sg.rb
Normal file
107
src/vnm_mad/remotes/lib/one_sg.rb
Normal file
@ -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
|
@ -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'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user