Here we are adding the methods to be able to find connected_subnets, dis/connect 2 subnets, find out all the related nics. The find_all_connected_nics function is interesting and needs further explanation. find_all_connected_nics simply find all the nics that are kind of related to the subnet we are in. For example, let's assume we have three subnets that are connected to each other in this way A<->B<->C. Let's also assume that each of these subnets have 1 nic. To be able to implement connected subnets, we need to implement IpsecTunnels in between nicA<->nicB AND nicB<->nicC. Furthermore, to be able to implement tunnel destroys in rekey nic tunnel, we remove old states to avoid leaving ip xfrm state objects dangling. Therefore, in a graph, if we are performing rekeying for a subset of tunnels, we need to rekey all the active tunnels. Otherwise, they will get dropped. For that reason, we find all the connected nics by performing depth first search. Let's also explain what we do to connect_subnet and disconnect_subnet. We first create a ConnectedSubnet entity, and then, we create tunnels from each nic in the existing subnet to every other nic in the connected_subnet. Therefore, if we have 3 nics in the current subnet and 2 nics in the other, we create 6 tunnels in total. We destroy these tunnels at the tume of disconnect_subnet. However, we try to be careful not to destroy tunnels that are in between the nics in the same subnet.
137 lines
4.4 KiB
Ruby
137 lines
4.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require_relative "../model"
|
|
|
|
class PrivateSubnet < Sequel::Model
|
|
many_to_many :vms, join_table: :nic, left_key: :private_subnet_id, right_key: :vm_id
|
|
one_to_many :nics, key: :private_subnet_id
|
|
one_to_one :strand, key: :id
|
|
many_to_many :firewalls
|
|
one_to_many :load_balancers
|
|
|
|
PRIVATE_SUBNET_RANGES = [
|
|
"10.0.0.0/8",
|
|
"172.16.0.0/12",
|
|
"192.168.0.0/16"
|
|
].freeze
|
|
|
|
BANNED_IPV4_SUBNETS = [
|
|
NetAddr::IPv4Net.parse("172.16.0.0/16"),
|
|
NetAddr::IPv4Net.parse("172.17.0.0/16"),
|
|
NetAddr::IPv4Net.parse("172.18.0.0/16")
|
|
].freeze
|
|
|
|
dataset_module Pagination
|
|
dataset_module Authorization::Dataset
|
|
include Authorization::HyperTagMethods
|
|
def hyper_tag_name(project)
|
|
"project/#{project.ubid}/location/#{display_location}/private-subnet/#{name}"
|
|
end
|
|
|
|
include Authorization::TaggableMethods
|
|
|
|
def connected_subnets
|
|
PrivateSubnet.where(
|
|
id: DB[:connected_subnet].where(subnet_id_1: id).or(subnet_id_2: id).select(Sequel.case({{subnet_id_1: id} => :subnet_id_2}, :subnet_id_1)).all.map(&:values).flatten
|
|
).all
|
|
end
|
|
|
|
def all_nics
|
|
nics + connected_subnets.flat_map(&:nics)
|
|
end
|
|
|
|
def destroy
|
|
DB.transaction do
|
|
FirewallsPrivateSubnets.where(private_subnet_id: id).all.each(&:destroy)
|
|
super
|
|
end
|
|
end
|
|
|
|
def display_location
|
|
LocationNameConverter.to_display_name(location)
|
|
end
|
|
|
|
def path
|
|
"/location/#{display_location}/private-subnet/#{name}"
|
|
end
|
|
|
|
include ResourceMethods
|
|
|
|
def self.ubid_to_name(ubid)
|
|
ubid.to_s[0..7]
|
|
end
|
|
|
|
def display_state
|
|
(state == "waiting") ? "available" : state
|
|
end
|
|
|
|
include SemaphoreMethods
|
|
semaphore :destroy, :refresh_keys, :add_new_nic, :update_firewall_rules
|
|
|
|
def self.random_subnet
|
|
subnet_dict = PRIVATE_SUBNET_RANGES.each_with_object({}) do |subnet, hash|
|
|
prefix_length = Integer(subnet.split("/").last, 10)
|
|
hash[subnet] = (2**16 + 2**12 + 2**8 - 2**prefix_length)
|
|
end
|
|
subnet_dict.max_by { |_, weight| rand**(1.0 / weight) }.first
|
|
end
|
|
|
|
# Here we are blocking the bottom 4 and top 1 addresses of each subnet
|
|
# The bottom first address is called the network address, that must be
|
|
# blocked since we use it for routing.
|
|
# The very last address is blocked because typically it is used as the
|
|
# broadcast address.
|
|
# We further block the bottom 3 addresses for future proofing. We may
|
|
# use it in future for some other purpose. AWS also does that. Here
|
|
# is the source;
|
|
# https://docs.aws.amazon.com/vpc/latest/userguide/subnet-sizing.html
|
|
def random_private_ipv4
|
|
total_hosts = 2**(32 - net4.netmask.prefix_len) - 5
|
|
random_offset = SecureRandom.random_number(total_hosts) + 4
|
|
addr = net4.nth_subnet(32, random_offset)
|
|
return random_private_ipv4 if nics.any? { |nic| nic.private_ipv4.to_s == addr.to_s }
|
|
|
|
addr
|
|
end
|
|
|
|
def random_private_ipv6
|
|
addr = net6.nth_subnet(79, SecureRandom.random_number(2**(79 - net6.netmask.prefix_len) - 2).to_i + 1)
|
|
return random_private_ipv6 if nics.any? { |nic| nic.private_ipv6.to_s == addr.to_s }
|
|
|
|
addr
|
|
end
|
|
|
|
def connect_subnet(subnet)
|
|
small_id_ps, large_id_ps = [self, subnet].sort_by(&:id)
|
|
ConnectedSubnet.create_with_id(subnet_id_1: small_id_ps.id, subnet_id_2: large_id_ps.id)
|
|
nics.each do |nic|
|
|
create_tunnels(subnet.nics, nic)
|
|
end
|
|
subnet.incr_refresh_keys
|
|
end
|
|
|
|
def disconnect_subnet(subnet)
|
|
small_id_ps, large_id_ps = [self, subnet].sort_by(&:id)
|
|
nics.each do |nic|
|
|
(nic.src_ipsec_tunnels + nic.dst_ipsec_tunnels).each do |tunnel|
|
|
tunnel.destroy if tunnel.src_nic.private_subnet_id == subnet.id || tunnel.dst_nic.private_subnet_id == subnet.id
|
|
end
|
|
end
|
|
ConnectedSubnet.where(subnet_id_1: small_id_ps.id, subnet_id_2: large_id_ps.id).all.map(&:destroy)
|
|
subnet.incr_refresh_keys
|
|
incr_refresh_keys
|
|
end
|
|
|
|
def create_tunnels(nics, src_nic)
|
|
nics.each do |dst_nic|
|
|
next if src_nic == dst_nic
|
|
IpsecTunnel.create_with_id(src_nic_id: src_nic.id, dst_nic_id: dst_nic.id) unless IpsecTunnel[src_nic_id: src_nic.id, dst_nic_id: dst_nic.id]
|
|
IpsecTunnel.create_with_id(src_nic_id: dst_nic.id, dst_nic_id: src_nic.id) unless IpsecTunnel[src_nic_id: dst_nic.id, dst_nic_id: src_nic.id]
|
|
end
|
|
end
|
|
|
|
def find_all_connected_nics(excluded_private_subnet_ids = [])
|
|
nics + connected_subnets.select { |subnet| !excluded_private_subnet_ids.include?(subnet.id) }.flat_map { _1.find_all_connected_nics(excluded_private_subnet_ids + [id]) }.uniq
|
|
end
|
|
end
|