Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump netaddr gem for CVE-2019-17383 #2369

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ PATH
logging (~> 2.2.2)
membrane (~> 1.1.0)
nats-pure (~> 0.6.2)
netaddr (~> 1.5.0)
netaddr (~> 2.0.5)
openssl
prometheus-client (~> 1.0.0)
puma
Expand Down Expand Up @@ -175,7 +175,7 @@ GEM
mysql2 (0.5.3)
nats-pure (0.6.2)
net-ssh (5.2.0)
netaddr (1.5.1)
netaddr (2.0.5)
netrc (0.11.0)
nio4r (2.5.8)
openssl (3.0.0)
Expand Down
2 changes: 1 addition & 1 deletion src/bosh-director/bosh-director.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Gem::Specification.new do |spec|
spec.add_dependency 'membrane', '~>1.1.0'
spec.add_dependency 'nats-pure', '~>0.6.2'
spec.add_dependency 'openssl'
spec.add_dependency 'netaddr', '~>1.5.0'
spec.add_dependency 'netaddr', '~>2.0.5'
spec.add_dependency 'prometheus-client','~>1.0.0'
spec.add_dependency 'puma'
spec.add_dependency 'rack-test', '~>0.6.2' # needed for console
Expand Down
10 changes: 5 additions & 5 deletions src/bosh-director/lib/bosh/director/cidr_range_combiner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ def combine_ranges(cidr_ranges)
private

def stringify_tuples(cidr_tuples)
cidr_tuples.map { |tuple| [tuple[0].ip, tuple[1].ip] }
cidr_tuples.map { |tuple| [tuple[0].to_s, tuple[1].to_s] }
end

def sort_ranges(reserved_ranges)
reserved_ranges.sort do |e1, e2|
e1.to_i <=> e2.to_i
e1.network.addr <=> e2.network.addr
end
end

def min_max_tuples(sorted_reserved_ranges)
sorted_reserved_ranges.map do |r|
[r.first(Objectify: true), r.last(Objectify: true)]
[r.nth(0), r.nth(r.len - 1)]
end
end

Expand All @@ -40,11 +40,11 @@ def combine_adjacent_ranges(range_tuples)
can_combine = false
break
end
if (range_tuple[1].succ == next_range_tuple[0])
if (range_tuple[1].next.addr == next_range_tuple[0].addr)
range_tuple[1] = next_range_tuple[1]
i += 1
# does not cover all cases: 10/32, 10/8
elsif ((range_tuple[0] < next_range_tuple[0]) && (range_tuple[1] > next_range_tuple[1]))
elsif ((range_tuple[0].addr < next_range_tuple[0].addr) && (range_tuple[1].addr > next_range_tuple[1].addr))
i += 1
else
can_combine = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def initialize(logger)
def delete(ip)
cidr_ip = CIDRIP.new(ip)

ip_address = Bosh::Director::Models::IpAddress.first(address_str: cidr_ip.to_i.to_s)
ip_address = Bosh::Director::Models::IpAddress.first(address_str: cidr_ip.stringify)

if ip_address
@logger.debug("Releasing ip '#{cidr_ip}'")
Expand Down Expand Up @@ -50,8 +50,8 @@ def allocate_dynamic_ip(reservation, subnet)
retry
end

@logger.debug("Allocated dynamic IP '#{ip_address.ip}' for #{reservation.network.name}")
ip_address.to_i
@logger.debug("Allocated dynamic IP '#{ip_address}' for #{reservation.network.name}")
ip_address.addr
end

def allocate_vip_ip(reservation, subnet)
Expand All @@ -68,29 +68,25 @@ def allocate_vip_ip(reservation, subnet)
retry
end

@logger.debug("Allocated vip IP '#{ip_address.ip}' for #{reservation.network.name}")
ip_address.to_i
@logger.debug("Allocated vip IP '#{ip_address}' for #{reservation.network.name}")
ip_address.addr
end

private

def try_to_allocate_dynamic_ip(reservation, subnet)
addresses_in_use = Set.new(all_ip_addresses)

first_range_address = subnet.range.first(Objectify: true).to_i - 1
first_range_address = subnet.range.network.addr - 1
addresses_we_cant_allocate = addresses_in_use
addresses_we_cant_allocate << first_range_address

addresses_we_cant_allocate.merge(subnet.restricted_ips.to_a) unless subnet.restricted_ips.empty?
addresses_we_cant_allocate.merge(subnet.static_ips.to_a) unless subnet.static_ips.empty?
addr = find_first_available_address(addresses_we_cant_allocate, first_range_address)
if subnet.range.version == 6
ip_address = NetAddr::CIDRv6.new(addr)
else
ip_address = NetAddr::CIDRv4.new(addr)
end
ip_address = CIDRIP.new(addr)

unless subnet.range == ip_address || subnet.range.contains?(ip_address)
unless subnet.range.contains(ip_address.netaddr)
raise NoMoreIPsAvailableAndStopRetrying
end

Expand All @@ -115,7 +111,7 @@ def try_to_allocate_vip_ip(reservation, subnet)

raise NoMoreIPsAvailableAndStopRetrying if available_ips.empty?

ip_address = NetAddr::CIDRv4.new(available_ips.first)
ip_address = CIDRIP.new(available_ips.first)

save_ip(ip_address, reservation, false)

Expand All @@ -130,7 +126,7 @@ def reserve_with_instance_validation(instance_model, ip, reservation, is_static)
# try to save IP first before validating its instance to prevent race conditions
save_ip(ip, reservation, is_static)
rescue IpFoundInDatabaseAndCanBeRetried
ip_address = Bosh::Director::Models::IpAddress.first(address_str: ip.to_i.to_s)
ip_address = Bosh::Director::Models::IpAddress.first(address_str: ip.stringify)

retry unless ip_address

Expand Down Expand Up @@ -161,7 +157,7 @@ def validate_instance_and_update_reservation_type(instance_model, ip, ip_address

def save_ip(ip, reservation, is_static)
ip_address = Bosh::Director::Models::IpAddress.new(
address_str: ip.to_i.to_s,
address_str: ip.stringify,
network_name: reservation.network.name,
task_id: Bosh::Director::Config.current_job.task_id,
static: is_static,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def self.parse(network_spec, availability_zones, logger)
end
subnets << new_subnet
end

validate_all_subnets_use_azs(subnets, name)
new(name, subnets, logger, managed)
end
Expand All @@ -46,15 +47,15 @@ def network_settings(reservation, default_properties = REQUIRED_DEFAULTS, availa
"Can't generate network settings without an IP"
end

ip = ip_to_netaddr(reservation.ip)
ip = format_ip(reservation.ip)
subnet = find_subnet_containing(reservation.ip)
unless subnet
raise NetworkReservationInvalidIp, "Provided IP '#{ip}' does not belong to any subnet"
end

config = {
"type" => "manual",
"ip" => ip.ip,
"ip" => ip,
"netmask" => subnet.netmask,
"cloud_properties" => subnet.cloud_properties
}
Expand All @@ -64,13 +65,13 @@ def network_settings(reservation, default_properties = REQUIRED_DEFAULTS, availa
end

config["dns"] = subnet.dns if subnet.dns
config["gateway"] = subnet.gateway.ip if subnet.gateway
config["gateway"] = subnet.gateway.to_s if subnet.gateway
config
end

def ip_type(cidr_ip)
static_ips = @subnets.map { |subnet| subnet.static_ips.to_a }.flatten
static_ips.include?(cidr_ip.to_i) ? :static : :dynamic
static_ips.include?(cidr_ip.addr) ? :static : :dynamic
end

def find_az_names_for_ip(ip)
Expand All @@ -87,7 +88,8 @@ def manual?
# @param [Integer, NetAddr::CIDR, String] ip
# @yield the subnet that contains the IP.
def find_subnet_containing(ip)
@subnets.find { |subnet| subnet.range.contains?(ip) }
ip = CIDRIP.parse(ip)
@subnets.find { |subnet| subnet.range.contains(ip) }
end

private
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
require 'netaddr'

module Bosh::Director
module DeploymentPlan
class ManualNetworkSubnet < Subnet
extend ValidationHelper
extend IpUtil
include IpUtil
FlorianNachtigall marked this conversation as resolved.
Show resolved Hide resolved

attr_reader :network_name, :name, :dns,
:availability_zone_names, :netmask_bits
Expand All @@ -25,38 +28,44 @@ def self.parse(network_name, subnet_spec, availability_zones, managed = false)
end

if range_property
range = NetAddr::CIDR.create(range_property)
range_wrapper = CIDR.new(range_property)
range = range_wrapper.netaddr

if range.size <= 1
if range.len <= 1
raise NetworkInvalidRange, "Invalid network range '#{range_property}', " \
'should include at least 2 IPs'
end

netmask = range.wildcard_mask
network_id = range.network(Objectify: true)
broadcast = range.version == 6 ? range.last(Objectify: true) : range.broadcast(Objectify: true)
netmask = range_wrapper.netmask
network_id = range.network
broadcast = range.nth(range.len - 1)

if gateway_property
gateway = NetAddr::CIDR.create(gateway_property)
invalid_gateway(network_name, 'must be a single IP') unless gateway.size == 1
invalid_gateway(network_name, 'must be inside the range') unless range.contains?(gateway)
invalid_gateway(network_name, "can't be the network id") if gateway == network_id
invalid_gateway(network_name, "can't be the broadcast IP") if gateway == broadcast
begin
gateway = CIDRIP.parse(gateway_property)
rescue NetAddr::ValidationError
invalid_gateway(network_name, 'not a valid IP format')
end

invalid_gateway(network_name, 'must be inside the range') unless range.contains(gateway)
invalid_gateway(network_name, "can't be the network id") if gateway.addr == network_id.addr
invalid_gateway(network_name, "can't be the broadcast IP") if gateway.addr == broadcast.addr
end

static_property = safe_property(subnet_spec, 'static', optional: true)

restricted_ips.add(gateway.to_i) if gateway
restricted_ips.add(network_id.to_i)
restricted_ips.add(broadcast.to_i)
restricted_ips.add(gateway.addr) if gateway
restricted_ips.add(network_id.addr)
restricted_ips.add(broadcast.addr)

each_ip(reserved_property) do |ip|
unless range.contains?(ip)
raise NetworkReservedIpOutOfRange, "Reserved IP '#{format_ip(ip)}' is out of " \
each_ip(reserved_property) do |ip_int|
ip = CIDRIP.parse(ip_int)
unless range.contains(ip)
raise NetworkReservedIpOutOfRange, "Reserved IP '#{ip.to_s}' is out of " \
"network '#{network_name}' range"
end

restricted_ips.add(ip)
restricted_ips.add(ip_int)
end

Config.director_ips&.each do |cidr|
Expand All @@ -70,7 +79,7 @@ def self.parse(network_name, subnet_spec, availability_zones, managed = false)
raise NetworkStaticIpOutOfRange, "Static IP '#{format_ip(ip)}' is in network '#{network_name}' reserved range"
end

unless range.contains?(ip)
unless range.contains(CIDRIP.parse(ip))
raise NetworkStaticIpOutOfRange, "Static IP '#{format_ip(ip)}' is out of network '#{network_name}' range"
end

Expand Down Expand Up @@ -115,17 +124,19 @@ def initialize(network_name, range, gateway, name_servers, cloud_properties, net

def overlaps?(subnet)
return false unless range && subnet.range
return false unless range.version == subnet.range.version

range == subnet.range ||
range.contains?(subnet.range) ||
subnet.range.contains?(range)
rescue NetAddr::VersionError
! range.rel(subnet.range).nil?
rescue NetAddr::ValidationError
false
end

def is_reservable?(ip)
range.contains?(ip) && !restricted_ips.include?(ip.to_i)
rescue NetAddr::VersionError
ip = CIDRIP.parse(ip) # TODO NETADDR: according to test should not be neccessary, ip should already be a IPv4 object, however method is sometimes used differently
return false unless ip.version == range.version

range.contains(ip) && !restricted_ips.include?(ip.addr)
rescue NetAddr::ValidationError
false
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ module Bosh::Director
module DeploymentPlan
module NetworkParser
class NameServersParser

include ValidationHelper
include IpUtil

def initialize()
dns_config = Config.dns || {}
Expand All @@ -19,13 +19,14 @@ def parse(network, subnet_properties)
if dns_spec
servers = []
dns_spec.each do |dns|
dns = NetAddr::CIDR.create(dns)
unless dns.size == 1
begin
dns = CIDRIP.parse(dns)
rescue NetAddr::ValidationError => e
raise NetworkInvalidDns,
"Invalid DNS for network '#{network}': must be a single IP"
"Invalid DNS for network '#{network}': #{e}"
end

servers << dns.ip
servers << dns.to_s
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def create_subnet(subnet, network_model, rollback)
network_cloud_properties = network_create_results[2]

range = subnet.range ? subnet.range.to_s : network_address_properties['range']
gw = subnet.gateway ? subnet.gateway.ip : network_address_properties['gateway']
gw = subnet.gateway ? subnet.gateway.to_s : network_address_properties['gateway']

reserved_ips = network_address_properties.fetch('reserved', [])
rollback[network_cid] = cpi
Expand All @@ -146,28 +146,30 @@ def fetch_cpi_input(subnet, az_cloud_props)
}
cpi_input['cloud_properties'] = az_cloud_props.merge(subnet.cloud_properties) if subnet.cloud_properties
cpi_input['range'] = subnet.range.to_s if subnet.range
cpi_input['gateway'] = subnet.gateway.ip if subnet.gateway
cpi_input['gateway'] = subnet.gateway.to_s if subnet.gateway
cpi_input['netmask_bits'] = subnet.netmask_bits if subnet.netmask_bits
cpi_input
end

def populate_subnet_properties(subnet, db_subnet)
cidr = CIDR.new(db_subnet.range)
subnet.cloud_properties = JSON.parse(db_subnet.cloud_properties)
subnet.range = NetAddr::CIDR.create(db_subnet.range)
subnet.gateway = NetAddr::CIDR.create(db_subnet.gateway)
subnet.netmask = subnet.range.wildcard_mask
network_id = subnet.range.network(Objectify: true)
broadcast = subnet.range.version == 6 ? subnet.range.last(Objectify: true) : subnet.range.broadcast(Objectify: true)
subnet.restricted_ips.add(subnet.gateway.to_i) if subnet.gateway
subnet.restricted_ips.add(network_id.to_i)
subnet.restricted_ips.add(broadcast.to_i)
each_ip(JSON.parse(db_subnet.reserved)) do |ip|
unless subnet.range.contains?(ip)
subnet.gateway = CIDRIP.parse(db_subnet.gateway)
subnet.range = cidr.netaddr
subnet.netmask = cidr.netmask
network_id = subnet.range.network
broadcast = subnet.range.nth(subnet.range.len - 1)
subnet.restricted_ips.add(subnet.gateway.addr) if subnet.gateway
subnet.restricted_ips.add(network_id.addr)
subnet.restricted_ips.add(broadcast.addr)
each_ip(JSON.parse(db_subnet.reserved)) do |ip_int|
ip = CIDRIP.parse(ip_int)
unless subnet.range.contains(ip)
raise NetworkReservedIpOutOfRange,
"Reserved IP '#{format_ip(ip)}' is out of " \
"Reserved IP '#{ip.to_s}' is out of " \
"subnet '#{subnet.name}' range"
end
subnet.restricted_ips.add(ip)
subnet.restricted_ips.add(ip_int)
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def network_settings(reservation, default_properties = REQUIRED_DEFAULTS, _avail

{
'type' => 'vip',
'ip' => ip_to_netaddr(reservation.ip).ip,
'ip' => format_ip(reservation.ip),
'cloud_properties' => @cloud_properties,
}
end
Expand Down
Loading