mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-21 14:50:08 +03:00
Merge branch 'feature-3175b'
This commit is contained in:
commit
e061f11f69
@ -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_ */
|
||||
|
17
install.sh
17
install.sh
@ -932,17 +932,22 @@ 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/vnm_driver.rb \
|
||||
src/vnm_mad/remotes/lib/vnmmad.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/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 \
|
||||
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 \
|
||||
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 \
|
||||
|
@ -28,6 +28,8 @@
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
#include <sys/types.h>
|
||||
#include <regex.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -14,15 +14,26 @@
|
||||
# limitations under the License. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
require 'OpenNebulaNetwork'
|
||||
require 'vnmmad'
|
||||
|
||||
class OpenNebulaHM < OpenNebulaNetwork
|
||||
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,10 +41,14 @@ class OpenNebulaHM < OpenNebulaNetwork
|
||||
unlock
|
||||
end
|
||||
|
||||
############################################################################
|
||||
# Activate the driver and creates bridges and tags devices as needed.
|
||||
############################################################################
|
||||
def activate
|
||||
lock
|
||||
|
||||
vm_id = @vm['ID']
|
||||
|
||||
process do |nic|
|
||||
bridge = nic[:bridge]
|
||||
dev = nic[:phydev]
|
||||
@ -66,23 +81,29 @@ class OpenNebulaHM < OpenNebulaNetwork
|
||||
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
|
||||
|
||||
def create_bridge(bridge)
|
||||
OpenNebula.exec_and_log("#{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
|
||||
`#{COMMANDS[:ip]} link show #{dev}`
|
||||
`#{command(:ip)} link show #{dev}`
|
||||
$?.exitstatus == 0
|
||||
end
|
||||
|
||||
def create_dev_vlan(dev, vlan)
|
||||
cmd = "#{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)
|
||||
@ -90,18 +111,20 @@ class OpenNebulaHM < OpenNebulaNetwork
|
||||
|
||||
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("#{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("#{COMMANDS[:ip]} link set #{dev} up")
|
||||
OpenNebula.exec_and_log("#{command(:ip)} link set #{dev} up")
|
||||
end
|
||||
end
|
@ -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
|
@ -14,9 +14,9 @@
|
||||
# limitations under the License. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
require 'OpenNebulaNetwork'
|
||||
require 'vnmmad'
|
||||
|
||||
class EbtablesVLAN < OpenNebulaNetwork
|
||||
class EbtablesVLAN < VNMMAD::VNMDriver
|
||||
DRIVER = "ebtables"
|
||||
|
||||
XPATH_FILTER = "TEMPLATE/NIC[VLAN='YES']"
|
||||
|
@ -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::VNMDriver.filter_driver(template64)
|
||||
filter_driver.deactivate
|
||||
|
@ -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::VNMDriver.filter_driver(template64, deploy_id)
|
||||
filter_driver.activate
|
||||
rescue Exception => e
|
||||
OpenNebula.log_error(e.message)
|
||||
|
@ -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::VNMDriver.filter_driver(template64)
|
||||
filter_driver.deactivate
|
||||
rescue Exception => e
|
||||
OpenNebula.log_error(e.message)
|
||||
|
@ -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::VNMDriver.filter_driver(template64, deploy_id)
|
||||
filter_driver.activate
|
||||
rescue Exception => e
|
||||
OpenNebula.log_error(e.message)
|
||||
|
77
src/vnm_mad/remotes/lib/address.rb
Normal file
77
src/vnm_mad/remotes/lib/address.rb
Normal file
@ -0,0 +1,77 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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 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 [Fixnum] The number of IPs in the range
|
||||
#
|
||||
# @return [Array<String>] 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
|
86
src/vnm_mad/remotes/lib/command.rb
Normal file
86
src/vnm_mad/remotes/lib/command.rb
Normal file
@ -0,0 +1,86 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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 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.
|
||||
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<String>] Arguments for the command
|
||||
def add (cmd, *args)
|
||||
if COMMANDS.keys.include?(cmd.to_sym)
|
||||
cmd_str = "#{COMMANDS[cmd.to_sym]} #{args.join(' ')}"
|
||||
else
|
||||
cmd_str = "#{cmd} #{args.join(' ')}"
|
||||
end
|
||||
|
||||
self << 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
|
||||
end
|
||||
|
||||
end
|
199
src/vnm_mad/remotes/lib/fw_driver.rb
Normal file
199
src/vnm_mad/remotes/lib/fw_driver.rb
Normal file
@ -0,0 +1,199 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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 FWDriver < VNMDriver
|
||||
# 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
|
||||
|
||||
# Method to clean iptables chains
|
||||
def deactivate
|
||||
lock
|
||||
|
||||
vm_id = @vm['ID']
|
||||
process do |nic|
|
||||
chain = "one-#{vm_id}-#{nic[:network_id]}"
|
||||
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)
|
||||
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 = `#{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
|
||||
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 =`#{command(:iptables)} -nL`
|
||||
chains = iptables_nl.scan(/(one-.*?) .*references/).flatten
|
||||
chains.include? chain
|
||||
end
|
||||
|
||||
def rule(rule)
|
||||
"#{command(:iptables)} #{rule}"
|
||||
end
|
||||
end
|
||||
end
|
154
src/vnm_mad/remotes/lib/nic.rb
Normal file
154
src/vnm_mad/remotes/lib/nic.rb
Normal file
@ -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='<mac>']/../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
|
222
src/vnm_mad/remotes/lib/security_groups.rb
Normal file
222
src/vnm_mad/remotes/lib/security_groups.rb
Normal file
@ -0,0 +1,222 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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 implement SecurityGroups
|
||||
module VNMNetwork
|
||||
|
||||
############################################################################
|
||||
# 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 '<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
|
||||
############################################################################
|
||||
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
|
||||
]
|
||||
|
||||
# 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
|
||||
|
||||
# Process the rule and generates the associated commands of the rule
|
||||
# @param [Commands] cmd to add the rule commands to
|
||||
# @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
|
||||
|
||||
# Return the network blocks associated to the rule
|
||||
# @return [Array<String>] each network block in CIDR.
|
||||
def net
|
||||
return [] if @ip.nil? || @size.nil?
|
||||
|
||||
VNMNetwork::to_nets(@ip, @size.to_i)
|
||||
end
|
||||
|
||||
# Expand the ICMP type with associated codes if any
|
||||
# @return [Array<String>] 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
|
||||
else
|
||||
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
|
||||
|
||||
############################################################################
|
||||
# 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<Hash>] to be applied to the NIC
|
||||
def initialize(vm, nic, sg_id, rules)
|
||||
@vm = vm
|
||||
@nic = nic
|
||||
@sg_id = sg_id
|
||||
|
||||
@rules = []
|
||||
@vars = {}
|
||||
|
||||
@commands = VNMNetwork::Commands.new
|
||||
|
||||
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
|
||||
|
||||
# Generates the iptables/ipset commands to implement this security group
|
||||
def process_rules
|
||||
@rules.each do |rule|
|
||||
rule.process(@commands, @vars)
|
||||
end
|
||||
|
||||
@commands.uniq!
|
||||
end
|
||||
|
||||
# Execute the implementation commands, process_rules MUST be called
|
||||
# before this method
|
||||
def run!
|
||||
@commands.run!
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
341
src/vnm_mad/remotes/lib/security_groups_iptables.rb
Normal file
341
src/vnm_mad/remotes/lib/security_groups_iptables.rb
Normal file
@ -0,0 +1,341 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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 < VNMNetwork::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.add :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.add :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.add :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.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.add :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.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.add :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_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.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.add :ipset, "add -exist #{set} #{n},icmp:#{type_code}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
############################################################################
|
||||
# This class represents a SecurityGroup implemented with iptables/ipset
|
||||
# Kernel facilities.
|
||||
############################################################################
|
||||
class SecurityGroupIPTables < VNMNetwork::SecurityGroup
|
||||
def initialize(vm, nic, sg_id, rules)
|
||||
super
|
||||
|
||||
@vars = SGIPTables.vars(@vm, @nic, @sg_id)
|
||||
end
|
||||
|
||||
def new_rule(rule)
|
||||
RuleIPTables.new(rule)
|
||||
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 = VNMNetwork::Commands.new
|
||||
|
||||
commands.add :iptables, "-S"
|
||||
iptables_s = commands.run!
|
||||
|
||||
iptables_forwards = ""
|
||||
|
||||
if iptables_s.match(/^-N #{GLOBAL_CHAIN}$/)
|
||||
commands.add :iptables, "-L #{GLOBAL_CHAIN} --line-numbers"
|
||||
iptables_forwards = commands.run!
|
||||
end
|
||||
|
||||
commands.add :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 = VNMNetwork::Commands.new
|
||||
|
||||
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
|
||||
|
||||
# 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 = VNMNetwork::Commands.new
|
||||
|
||||
vars = SGIPTables.vars(vm, nic)
|
||||
|
||||
chain = vars[:chain]
|
||||
chain_in = vars[:chain_in]
|
||||
chain_out = vars[:chain_out]
|
||||
|
||||
# create chains
|
||||
commands.add :iptables, "-N #{chain_in}" # inbound
|
||||
commands.add :iptables, "-N #{chain_out}" # outbound
|
||||
|
||||
# Send traffic to the NIC chains
|
||||
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.add :iptables, "-A #{chain_out} -m mac ! --mac-source #{nic[:mac]} -j DROP"
|
||||
end
|
||||
|
||||
# IP-spofing
|
||||
if nic[:filter_ip_spoofing] == "YES"
|
||||
commands.add :iptables, "-A #{chain_out} ! --source #{nic[:ip]} -j DROP"
|
||||
end
|
||||
|
||||
# Related, Established
|
||||
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
|
||||
|
||||
# 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 = VNMNetwork::Commands.new
|
||||
commands.add :iptables, "-A #{chain_in} -j DROP"
|
||||
commands.add :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 = VNMNetwork::Commands.new
|
||||
|
||||
iptables_forwards.lines.reverse_each do |line|
|
||||
fields = line.split
|
||||
if [chain_in, chain_out].include?(fields[1])
|
||||
n = fields[0]
|
||||
commands.add :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.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.add :ipset, "destroy #{set}"
|
||||
end
|
||||
end
|
||||
|
||||
commands.run!
|
||||
end
|
||||
end
|
||||
|
||||
end
|
105
src/vnm_mad/remotes/lib/sg_driver.rb
Normal file
105
src/vnm_mad/remotes/lib/sg_driver.rb
Normal file
@ -0,0 +1,105 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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
|
||||
|
||||
############################################################################
|
||||
# OpenNebula Firewall with Security Groups Based on IPTables (KVM and Xen)
|
||||
############################################################################
|
||||
class SGDriver < VNMDriver
|
||||
|
||||
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 = VNMNetwork::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
|
||||
|
||||
# Activate the rules, bootstrap iptables chains and set filter rules for
|
||||
# each VM NIC
|
||||
def activate
|
||||
deactivate
|
||||
lock
|
||||
|
||||
# Global Bootstrap
|
||||
SGIPTables.global_bootstrap
|
||||
|
||||
# Process the rules
|
||||
@vm.nics.each do |nic|
|
||||
next if nic[:security_groups].nil?
|
||||
|
||||
SGIPTables.nic_pre(@vm, nic)
|
||||
|
||||
sg_ids = nic[:security_groups].split(",")
|
||||
|
||||
sg_ids.each do |sg_id|
|
||||
rules = @security_group_rules[sg_id]
|
||||
|
||||
sg = SGIPTables::SecurityGroupIPTables.new(@vm, nic, sg_id,
|
||||
rules)
|
||||
|
||||
begin
|
||||
sg.process_rules
|
||||
sg.run!
|
||||
rescue Exception => e
|
||||
unlock
|
||||
deactivate
|
||||
raise e
|
||||
end
|
||||
end
|
||||
|
||||
SGIPTables.nic_post(@vm, nic)
|
||||
end
|
||||
|
||||
unlock
|
||||
end
|
||||
|
||||
# Clean iptables rules and chains
|
||||
def deactivate
|
||||
lock
|
||||
|
||||
begin
|
||||
@vm.nics.each do |nic|
|
||||
SGIPTables.nic_deactivate(@vm, nic)
|
||||
end
|
||||
rescue Exception => e
|
||||
raise e
|
||||
ensure
|
||||
unlock
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
110
src/vnm_mad/remotes/lib/vm.rb
Normal file
110
src/vnm_mad/remotes/lib/vm.rb
Normal file
@ -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
|
158
src/vnm_mad/remotes/lib/vnm_driver.rb
Normal file
158
src/vnm_mad/remotes/lib/vnm_driver.rb
Normal file
@ -0,0 +1,158 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
################################################################################
|
||||
# The VNMMAD module provides the basic abstraction to implement custom
|
||||
# virtual network drivers. The VNMAD module includes:
|
||||
# - 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 FirewallDriver and SGDriver.
|
||||
############################################################################
|
||||
class VNMDriver
|
||||
attr_reader :hypervisor, :vm
|
||||
|
||||
# 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]
|
||||
# @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 = VNMNetwork::VM.new(REXML::Document.new(vm_tpl).root,
|
||||
xpath_filter, deploy_id, @hypervisor)
|
||||
end
|
||||
|
||||
# Creates a new VNDriver 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-<driver>-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<String>] 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[FWDriver::XPATH_FILTER].nil?
|
||||
end
|
||||
|
||||
# Returns a filter object based on the contents of the template
|
||||
#
|
||||
# @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)
|
||||
FWDriver.new(vm_xml, deploy_id, hypervisor)
|
||||
else
|
||||
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
|
49
src/vnm_mad/remotes/lib/vnmmad.rb
Normal file
49
src/vnm_mad/remotes/lib/vnmmad.rb
Normal file
@ -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 'vnm_driver'
|
||||
require 'fw_driver'
|
||||
require 'sg_driver'
|
||||
|
||||
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"
|
@ -14,9 +14,9 @@
|
||||
# limitations under the License. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
require 'OpenNebulaNetwork'
|
||||
require 'vnmmad'
|
||||
|
||||
class OpenvSwitchVLAN < OpenNebulaNetwork
|
||||
class OpenvSwitchVLAN < VNMMAD::VNMDriver
|
||||
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 = "#{command(: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 = "#{command(: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 = "#{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 < OpenNebulaNetwork
|
||||
def add_flow(filter,action,priority=nil)
|
||||
priority = (priority.to_s.empty? ? "" : "priority=#{priority},")
|
||||
|
||||
run "#{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 "#{COMMANDS[:ovs_ofctl]} del-flows " <<
|
||||
run "#{command(: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 = `#{command(:ovs_ofctl)} \
|
||||
dump-ports #{@nic[:bridge]} #{@nic[:tap]}`
|
||||
|
||||
@nic[:port] = dump_ports.scan(/^\s*port\s*(\d+):/).flatten.first
|
||||
|
146
src/vnm_mad/remotes/test/sg_test_pre.rb
Executable file
146
src/vnm_mad/remotes/test/sg_test_pre.rb
Executable file
@ -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=<<EOF
|
||||
<VM>
|
||||
<ID>3</ID>
|
||||
<TEMPLATE>
|
||||
<NIC>
|
||||
<AR_ID><![CDATA[0]]></AR_ID>
|
||||
<BRIDGE><![CDATA[vbr0]]></BRIDGE>
|
||||
<FILTER_IP_SPOOFING><![CDATA[YES]]></FILTER_IP_SPOOFING>
|
||||
<FILTER_MAC_SPOOFING><![CDATA[YES]]></FILTER_MAC_SPOOFING>
|
||||
<IP><![CDATA[10.0.0.7]]></IP>
|
||||
<MAC><![CDATA[02:00:0a:00:00:07]]></MAC>
|
||||
<NETWORK><![CDATA[test]]></NETWORK>
|
||||
<NETWORK_ID><![CDATA[0]]></NETWORK_ID>
|
||||
<NETWORK_UNAME><![CDATA[ruben]]></NETWORK_UNAME>
|
||||
<NIC_ID><![CDATA[0]]></NIC_ID>
|
||||
<SECURITY_GROUPS><![CDATA[100]]></SECURITY_GROUPS>
|
||||
<VLAN><![CDATA[NO]]></VLAN>
|
||||
</NIC>
|
||||
<SECURITY_GROUP_RULE>
|
||||
<PROTOCOL><![CDATA[TCP]]></PROTOCOL>
|
||||
<RULE_TYPE><![CDATA[outbound]]></RULE_TYPE>
|
||||
<SECURITY_GROUP_ID><![CDATA[100]]></SECURITY_GROUP_ID>
|
||||
<SECURITY_GROUP_NAME><![CDATA[Test]]></SECURITY_GROUP_NAME>
|
||||
</SECURITY_GROUP_RULE>
|
||||
<SECURITY_GROUP_RULE>
|
||||
<PROTOCOL><![CDATA[TCP]]></PROTOCOL>
|
||||
<RANGE><![CDATA[80,22]]></RANGE>
|
||||
<RULE_TYPE><![CDATA[inbound]]></RULE_TYPE>
|
||||
<SECURITY_GROUP_ID><![CDATA[100]]></SECURITY_GROUP_ID>
|
||||
<SECURITY_GROUP_NAME><![CDATA[Test]]></SECURITY_GROUP_NAME>
|
||||
</SECURITY_GROUP_RULE>
|
||||
<SECURITY_GROUP_RULE>
|
||||
<ICMP_TYPE><![CDATA[8]]></ICMP_TYPE>
|
||||
<PROTOCOL><![CDATA[ICMP]]></PROTOCOL>
|
||||
<RULE_TYPE><![CDATA[inbound]]></RULE_TYPE>
|
||||
<SECURITY_GROUP_ID><![CDATA[100]]></SECURITY_GROUP_ID>
|
||||
<SECURITY_GROUP_NAME><![CDATA[Test]]></SECURITY_GROUP_NAME>
|
||||
</SECURITY_GROUP_RULE>
|
||||
<SECURITY_GROUP_RULE>
|
||||
<AR_ID><![CDATA[0]]></AR_ID>
|
||||
<ICMP_TYPE><![CDATA[0]]></ICMP_TYPE>
|
||||
<IP><![CDATA[10.0.0.7]]></IP>
|
||||
<MAC><![CDATA[02:00:0a:00:00:07]]></MAC>
|
||||
<NETWORK_ID><![CDATA[0]]></NETWORK_ID>
|
||||
<PROTOCOL><![CDATA[ICMP]]></PROTOCOL>
|
||||
<RULE_TYPE><![CDATA[outbound]]></RULE_TYPE>
|
||||
<SECURITY_GROUP_ID><![CDATA[100]]></SECURITY_GROUP_ID>
|
||||
<SECURITY_GROUP_NAME><![CDATA[Test]]></SECURITY_GROUP_NAME>
|
||||
<SIZE><![CDATA[27]]></SIZE>
|
||||
<TYPE><![CDATA[IP4]]></TYPE>
|
||||
</SECURITY_GROUP_RULE>
|
||||
<SECURITY_GROUP_RULE>
|
||||
<IP><![CDATA[192.168.10.3]]></IP>
|
||||
<PROTOCOL><![CDATA[TCP]]></PROTOCOL>
|
||||
<RANGE><![CDATA[80:100,22]]></RANGE>
|
||||
<RULE_TYPE><![CDATA[inbound]]></RULE_TYPE>
|
||||
<SECURITY_GROUP_ID><![CDATA[100]]></SECURITY_GROUP_ID>
|
||||
<SECURITY_GROUP_NAME><![CDATA[Test]]></SECURITY_GROUP_NAME>
|
||||
<SIZE><![CDATA[23]]></SIZE>
|
||||
</SECURITY_GROUP_RULE>
|
||||
<SECURITY_GROUP_RULE>
|
||||
<AR_ID><![CDATA[0]]></AR_ID>
|
||||
<ICMP_TYPE><![CDATA[3]]></ICMP_TYPE>
|
||||
<IP><![CDATA[10.0.0.7]]></IP>
|
||||
<MAC><![CDATA[02:00:0a:00:00:07]]></MAC>
|
||||
<NETWORK_ID><![CDATA[0]]></NETWORK_ID>
|
||||
<PROTOCOL><![CDATA[ICMP]]></PROTOCOL>
|
||||
<RULE_TYPE><![CDATA[outbound]]></RULE_TYPE>
|
||||
<SECURITY_GROUP_ID><![CDATA[100]]></SECURITY_GROUP_ID>
|
||||
<SECURITY_GROUP_NAME><![CDATA[Test]]></SECURITY_GROUP_NAME>
|
||||
<SIZE><![CDATA[27]]></SIZE>
|
||||
<TYPE><![CDATA[IP4]]></TYPE>
|
||||
</SECURITY_GROUP_RULE>
|
||||
<SECURITY_GROUP_RULE>
|
||||
<IP><![CDATA[172.168.0.0]]></IP>
|
||||
<PROTOCOL><![CDATA[UDP]]></PROTOCOL>
|
||||
<RULE_TYPE><![CDATA[outbound]]></RULE_TYPE>
|
||||
<SECURITY_GROUP_ID><![CDATA[100]]></SECURITY_GROUP_ID>
|
||||
<SECURITY_GROUP_NAME><![CDATA[Test]]></SECURITY_GROUP_NAME>
|
||||
<SIZE><![CDATA[255]]></SIZE>
|
||||
</SECURITY_GROUP_RULE>
|
||||
<TEMPLATE_ID><![CDATA[0]]></TEMPLATE_ID>
|
||||
<VMID><![CDATA[0]]></VMID>
|
||||
</TEMPLATE>
|
||||
</VM>
|
||||
EOF
|
||||
|
||||
one_sg = VNMMAD::SGDriver.new(vm_xml, "one-0", "test")
|
||||
one_sg.activate
|
@ -34,9 +34,9 @@ $: << RUBY_LIB_LOCATION
|
||||
|
||||
require 'yaml'
|
||||
require 'CommandManager'
|
||||
require 'OpenNebulaNetwork'
|
||||
require 'vnmmad'
|
||||
|
||||
class OpenNebulaVMware < OpenNebulaNetwork
|
||||
class OpenNebulaVMware < VNMMAD::VNMDriver
|
||||
DRIVER = "vmware"
|
||||
|
||||
XPATH_FILTER = "TEMPLATE/NIC"
|
||||
|
Loading…
x
Reference in New Issue
Block a user