class OpenVPNConfigurator::RouteGatherer

Constants

AWS_IP_RANGES_URL

Public Instance Methods

extend_template(options) click to toggle source
# File lib/openvpn_configurator/route_gatherer.rb, line 20
def extend_template(options)
        template = read_file options[:input_path]
        old_output = read_file options[:output_path], nonexistent_behavior: :empty_string

        routes = gather_routes options
        reduced = reduce_routes routes
        rendered = render_routes reduced, client: !!options[:client]

        result = format "%s\n\n\n# Added by OpenVPN Configurator v%s at %s\n%s", template, VERSION, Time.now.iso8601(6), rendered
        if result != old_output
                logger.info "Output content changed, rewriting file #{options[:output_path].inspect}"
                write_file options[:output_path], result
                change_actions options
        else
                logger.info 'Output content unchanged, no actions taken.'
        end
end

Private Instance Methods

aws_ip_ranges() click to toggle source
# File lib/openvpn_configurator/route_gatherer.rb, line 42
def aws_ip_ranges
        @aws_ip_ranges ||= begin
                logger.trace "Fetching AWS IP ranges from #{AWS_IP_RANGES_URL.inspect}"
                data = RestClient.get AWS_IP_RANGES_URL
                JSON.parse data, symbolize_names: true
        end
end
change_actions(options) click to toggle source
# File lib/openvpn_configurator/route_gatherer.rb, line 51
def change_actions(options)
        options[:restart_systemd].each { |service| restart_systemd service }
end
gather_routes(options) click to toggle source

@return [Hash<String, Array<NetAddr::IPv4Net, NetAddr::IPv6Net>]

# File lib/openvpn_configurator/route_gatherer.rb, line 57
def gather_routes(options)
        result = {}
        options[:route_v4_aws_region].each { |region| result.merge! gather_v4_aws_region(region) }
        options[:route_v4_dns].each { |name| result.merge! gather_v4_dns(name) }
        result
end
gather_v4_aws_region(region) click to toggle source
# File lib/openvpn_configurator/route_gatherer.rb, line 65
def gather_v4_aws_region(region)
        networks = aws_ip_ranges[:prefixes].select { |p| p[:region] == region }
        routes = networks.map { |p| NetAddr.parse_net p[:ip_prefix] }
        {
                "route-v4-aws-region=#{region}" => routes
        }
end
gather_v4_dns(name) click to toggle source
# File lib/openvpn_configurator/route_gatherer.rb, line 74
def gather_v4_dns(name)
        {
                "route-v4-dns=#{name}" => resolve_v4(name).map { |a| NetAddr.parse_net "#{a}/32" }
        }
end
read_file(path, nonexistent_behavior: :raise) click to toggle source
# File lib/openvpn_configurator/route_gatherer.rb, line 81
def read_file(path, nonexistent_behavior: :raise)
        File.read path
rescue Errno::ENOENT => e
        case nonexistent_behavior
                when :empty_string
                        ''
                else
                        logger.error "Error reading file #{path.inspect}", e
                        raise
        end
end
reduce_routes(routes) click to toggle source

@return [Hash<String, Array<NetAddr::IPv4Net, NetAddr::IPv6Net>]

# File lib/openvpn_configurator/route_gatherer.rb, line 101
def reduce_routes(routes)
        seen_v4 = {}
        seen_v6 = {}
        reduction = false
        result = {}
        routes.each_pair do |name, entries|
                result[name] = []
                entries.each do |entry|
                        seen = case entry
                                when NetAddr::IPv4Net
                                        seen_v4
                                when NetAddr::IPv6Net
                                        seen_v6
                                else
                                        nil
                        end
                        if seen
                                covered = nil
                                seen.each_pair do |seen_name, seen_entries|
                                        found_entry = seen_entries.find { |s| [1, 0].include? s.rel(entry) }
                                        if found_entry
                                                covered = [seen_name, found_entry]
                                                break
                                        end
                                end
                                if covered
                                        reduction = true
                                        result[name].push Comment.new("Route #{entry} already covered by route #{covered[1]} (#{covered[0]})")
                                else
                                        seen[name] ||= []
                                        seen[name].push entry
                                        result[name].push entry
                                end
                        else
                                result[name].push entry
                        end
                end
        end
        if reduction
                reduce_routes result
        else
                result
        end
end
render_routes(routes, client: false) click to toggle source

@param routes [Hash<String, Array<NetAddr::IPv4Net, NetAddr::IPv6Net>>] { “Configuration Source Name” => [NetAddr::IPv4Net, NetAddr::IPv6Net] }

# File lib/openvpn_configurator/route_gatherer.rb, line 167
def render_routes(routes, client: false)
        indent = "\t\t\t"
        directive_indent = client ? indent : ' '
        prefix = client ? '' : "push#{indent}\""
        postfix = client ? '' : '"'

        result = []
        routes.keys.sort.each do |source|
                result << "##{source}"
                routes[source].sort_by(&:to_s).each do |route|
                        directive = case route
                                when NetAddr::IPv4Net
                                        format '%sroute%s%s%s', prefix, directive_indent, route.extended, postfix
                                when NetAddr::IPv6Net
                                        format '%sroute-ipv6%s%s%s', prefix, directive_indent, route.to_s, postfix
                                when Comment
                                        format '# %s', route.to_s
                                else
                                        raise "Only supporting IPv4 and IPv6 networks presently, got #{route.inspect} instead"
                        end
                        result << directive
                end
                result << ''
        end
        result.join "\n"
end
resolve_v4(name) click to toggle source

For tests, this method is stubbable @return [Array<NetAddr::IPv4, NetAddr::IPv6>]

# File lib/openvpn_configurator/route_gatherer.rb, line 149
def resolve_v4(name)
        logger.trace "Performing DNS lookup for hostname #{name.inspect}"
        Resolv.getaddresses(name).map { |a| NetAddr.parse_ip a }.select { |a| a.is_a? NetAddr::IPv4 }
end
restart_systemd(service_name) click to toggle source
# File lib/openvpn_configurator/route_gatherer.rb, line 155
def restart_systemd(service_name)
        logger.info "Restarting systemd service #{service_name.inspect}"
        system 'systemctl', 'restart', service_name
end
write_file(path, contents) click to toggle source
# File lib/openvpn_configurator/route_gatherer.rb, line 161
def write_file(path, contents)
        File.write path, contents
end