diff --git a/registry.json b/registry.json index 7547299..48f4782 100644 --- a/registry.json +++ b/registry.json @@ -13,8 +13,10 @@ "host": { "as": "4242420092", "v4_tunnel": "172.23.75.1", - "start-port": 5001, - "end-port": 5020 + "start_port": 5001, + "end_port": 5020, + "v4_public": "dn42.higgsboson.tk", + "v6_public": "dn42.higgsboson.tk" }, "network": { "hax404": { @@ -56,7 +58,7 @@ "flatbert": { "type": "openvpn", "proto": "udp", - "float": true, + "remote": "float", "v4_tunnel": "172.22.99.253", "lport": 5002 }, diff --git a/scripts/openvpn b/scripts/openvpn index 4729d14..eacdc04 100755 --- a/scripts/openvpn +++ b/scripts/openvpn @@ -1,43 +1,191 @@ -#!/usr/bin/ruby +#!/usr/bin/env ruby require_relative "utils" +require "netaddr" +require "optparse" +require "set" -template_path = Pathname.new(File.expand_path("../../templates", __FILE__)) -openvpn_path = Pathname.new(File.expand_path("../../openvpn", __FILE__)) -openvpn_template = Template.new(template_path.join("openvpn.conf.erb")) +class OpenvpnRegistry < Registry + def initialize + super + @host = data["host"] + @v4_tunnel_ip = @host["v4_tunnel"] + @start_port = @host["start_port"].to_i + @end_port = @host["end_port"].to_i -registry = Registry.new - -host = registry.data["host"] -host["v4_tunnel"] || die("v4_tunnel not set for host") - -registry.data["network"].each do |name, data| - next unless data["type"] == "openvpn" - key = openvpn_path.join("#{name}.key") - unless File.exists?(key) - sh("openvpn", "--genkey", "--secret", key) + @openvpn_path = Pathname.new(File.expand_path("../../openvpn", __FILE__)) + die("v4_tunnel not set for host") unless @v4_tunnel_ip end - required_params = [:proto, :lport, :tunnel_v4] - unless data["float"] - required_params += [:remote, :rport] + + def add_peer(name, peer) + save_config(name, generate_config(name, peer)) + + peer_template = generate_config(name, peer_params(peer)) + puts "==== openvpn.conf for #{name} =====" + puts peer_template + puts "===================================\n" end - required_params.each do |param| - unless data[param.to_s] - die "#{param.to_s} not set for peer #{name}" + + def remove_peer(peer) + if data["network"].delete(peer).nil? + die("failed to remove '#{peer}': no such peer found in registry.json") + end + key = @openvpn_path.join("#{name}.key") + unless File.exists?(key) + FileUtils.rm_f(key) end end - context = data.merge(own_v4_tunnel: host["v4_tunnel"]) - atomic_write(openvpn_path.join("#{name}.conf"), openvpn_template.render(context)) + def update_configurations + data["network"].each do |name, peer| + next unless peer["type"] == "openvpn" + save_config(name, generate_config(name, peer)) + end + end + + def generate_config(name, peer) + key = @openvpn_path.join("#{name}.key") + unless File.exists?(key) + FileUtils.mkdir_p(File.dirname(key)) + sh("openvpn", "--genkey", "--secret", key.to_s) + puts "===== shared key for #{name} ======" + puts File.read(key) + puts "===================================\n" + end + + context = template_params(name, peer) + + template_path = Pathname.new(File.expand_path("../../templates", __FILE__)) + openvpn_template = Template.new(template_path.join("openvpn.conf.erb")) + openvpn_template.render(context) + end + + private + def save_config(peer, template) + atomic_write(@openvpn_path.join("#{peer}.conf"), template) + end + + def peer_params(peer) + params = peer.dup + params["rport"] = peer["lport"] + if peer["rport"] + params["lport"] = peer["rport"] + end + params["remote"] = if peer["proto"] == "udp6" + data["host"]["v6_public"] + else + data["host"]["v4_public"] + end + params + end + + def template_params(name, params) + unless params["proto"] + die "proto not set for peer #{name}" + end + unless params["v4_tunnel"] # TODO + die "v4_tunnel not set for peer #{name}" + end + + params["lport"] ||= next_free_port + + if params["remote"] == "float" + params["rport"] ||= params["lport"] + end + + params.merge(own_v4_tunnel: @v4_tunnel_ip) + end + + def next_free_port + ports = Set.new(data["network"].map { |name, peer| peer["lport"] }) + (@start_port..@end_port).each do |port| + unless ports.include?(port) + return port + end + end + die "no free local ports in range #{@start_port}:#{@end_port}" + end end GLOBAL_OPTIONS = OptionParser.new do |opts| - opts.banner = "Usage: dhcp [options] [subcommand [options]]" + opts.banner = "Usage: openvpn [options] [subcommand [options]]" opts.separator "" opts.separator < +<% if remote == "float" -%> float port <%= lport %> -<% else %> +<% else -%> remote <%= remote %> rport <%= rport %> lport <%= lport %> -<% end %> +<% end -%> ifconfig <%= own_v4_tunnel %> <%= v4_tunnel %> secret /etc/openvpn/<%= name %>.key