add interactive openvpn command
This commit is contained in:
parent
b540709a35
commit
af94ab692f
@ -13,8 +13,10 @@
|
|||||||
"host": {
|
"host": {
|
||||||
"as": "4242420092",
|
"as": "4242420092",
|
||||||
"v4_tunnel": "172.23.75.1",
|
"v4_tunnel": "172.23.75.1",
|
||||||
"start-port": 5001,
|
"start_port": 5001,
|
||||||
"end-port": 5020
|
"end_port": 5020,
|
||||||
|
"v4_public": "dn42.higgsboson.tk",
|
||||||
|
"v6_public": "dn42.higgsboson.tk"
|
||||||
},
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"hax404": {
|
"hax404": {
|
||||||
@ -56,7 +58,7 @@
|
|||||||
"flatbert": {
|
"flatbert": {
|
||||||
"type": "openvpn",
|
"type": "openvpn",
|
||||||
"proto": "udp",
|
"proto": "udp",
|
||||||
"float": true,
|
"remote": "float",
|
||||||
"v4_tunnel": "172.22.99.253",
|
"v4_tunnel": "172.22.99.253",
|
||||||
"lport": 5002
|
"lport": 5002
|
||||||
},
|
},
|
||||||
|
200
scripts/openvpn
200
scripts/openvpn
@ -1,43 +1,191 @@
|
|||||||
#!/usr/bin/ruby
|
#!/usr/bin/env ruby
|
||||||
require_relative "utils"
|
require_relative "utils"
|
||||||
|
require "netaddr"
|
||||||
|
require "optparse"
|
||||||
|
require "set"
|
||||||
|
|
||||||
template_path = Pathname.new(File.expand_path("../../templates", __FILE__))
|
class OpenvpnRegistry < Registry
|
||||||
openvpn_path = Pathname.new(File.expand_path("../../openvpn", __FILE__))
|
def initialize
|
||||||
openvpn_template = Template.new(template_path.join("openvpn.conf.erb"))
|
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
|
@openvpn_path = Pathname.new(File.expand_path("../../openvpn", __FILE__))
|
||||||
|
die("v4_tunnel not set for host") unless @v4_tunnel_ip
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
required_params = [:proto, :lport, :tunnel_v4]
|
|
||||||
unless data["float"]
|
def add_peer(name, peer)
|
||||||
required_params += [:remote, :rport]
|
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
|
end
|
||||||
required_params.each do |param|
|
|
||||||
unless data[param.to_s]
|
def remove_peer(peer)
|
||||||
die "#{param.to_s} not set for peer #{name}"
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
context = data.merge(own_v4_tunnel: host["v4_tunnel"])
|
def update_configurations
|
||||||
atomic_write(openvpn_path.join("#{name}.conf"), openvpn_template.render(context))
|
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
|
end
|
||||||
|
|
||||||
GLOBAL_OPTIONS = OptionParser.new do |opts|
|
GLOBAL_OPTIONS = OptionParser.new do |opts|
|
||||||
opts.banner = "Usage: dhcp [options] [subcommand [options]]"
|
opts.banner = "Usage: openvpn [options] [subcommand [options]]"
|
||||||
opts.separator ""
|
opts.separator ""
|
||||||
opts.separator <<HELP
|
opts.separator <<HELP
|
||||||
Available subcommands:
|
Available subcommands:
|
||||||
add [options] NAME MACADDRESS: add dhcp lease
|
add [options] NAME PROTO REMOTE_ADDRESS|float: add openvpn peer
|
||||||
remove [options] NAME: remove dhcp static lease
|
remove [options] NAME: remove openvpn peer
|
||||||
|
regenerate: regenerate all openvpn configurations
|
||||||
|
|
||||||
See 'dhcp COMMAND --help' for more information on a specific command.
|
See 'openvpn COMMAND --help' for more information on a specific command.
|
||||||
HELP
|
HELP
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class Application
|
||||||
|
def run(args)
|
||||||
|
GLOBAL_OPTIONS.order!(args)
|
||||||
|
@registry = OpenvpnRegistry.new
|
||||||
|
case command = args.shift
|
||||||
|
when "add"
|
||||||
|
add_command(args)
|
||||||
|
when "remove"
|
||||||
|
remove_command(args)
|
||||||
|
when nil
|
||||||
|
puts(GLOBAL_OPTIONS.help())
|
||||||
|
exit(0)
|
||||||
|
when "regenerate" # fall through
|
||||||
|
else
|
||||||
|
die "unknown subcommand #{command}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@registry.save
|
||||||
|
@registry.update_configurations
|
||||||
|
end
|
||||||
|
def add_command(args)
|
||||||
|
options = {}
|
||||||
|
parser = OptionParser.new do |opts|
|
||||||
|
opts.banner = "Usage: openvpn add [options] NAME PROTO REMOTE_ADDRESS|float"
|
||||||
|
opts.on("--local-port PORT", Integer, "local openvpn port") do |port|
|
||||||
|
options["lport"] = port
|
||||||
|
end
|
||||||
|
opts.on("--remote-port PORT", Integer, "remote openvpn port") do |port|
|
||||||
|
options["rport"] = port
|
||||||
|
end
|
||||||
|
opts.on("-4", "--v4-tunnel IP", String, "remote v4 tunnel ip") do |address|
|
||||||
|
options["v4_tunnel"] = address
|
||||||
|
end
|
||||||
|
opts.on("-6", "--v6-tunnel IP", String, "remote v6 tunnel ip") do |address|
|
||||||
|
die("remote ipv6 tunnel ips are currently not implemented")
|
||||||
|
options["v6_tunnel"] = address
|
||||||
|
end
|
||||||
|
opts.on("--as AS", String, "remote autonomous system number") do |as|
|
||||||
|
options["as"] = as
|
||||||
|
end
|
||||||
|
end.order!(args)
|
||||||
|
if ARGV.size < 3
|
||||||
|
$stderr.puts "no enough arguments"
|
||||||
|
die(parser.help)
|
||||||
|
end
|
||||||
|
unless options["v4_tunnel"] || options["v6_tunnel"]
|
||||||
|
die("at least of one of these options have to specified: "+
|
||||||
|
"v4-tunnel-ip or v6-tunnel-ip")
|
||||||
|
end
|
||||||
|
name, proto, remote_address = args
|
||||||
|
options["remote"] = remote_address
|
||||||
|
unless ["udp", "udp6"].include?(proto)
|
||||||
|
die("proto must be either udp or udp6")
|
||||||
|
end
|
||||||
|
options["proto"] = proto
|
||||||
|
@registry.add_peer(name, options)
|
||||||
|
end
|
||||||
|
def remove_command(args)
|
||||||
|
parser = OptionParser.new do |opts|
|
||||||
|
opts.banner = "Usage: openvpn remove NAME"
|
||||||
|
end.order!(args)
|
||||||
|
if args.empty?
|
||||||
|
$stderr.puts "no enough arguments"
|
||||||
|
die(parser.help)
|
||||||
|
end
|
||||||
|
@registry.remove_peer(args.first)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Application.new.run(ARGV)
|
||||||
|
@ -9,14 +9,14 @@ persist-tun
|
|||||||
user nobody
|
user nobody
|
||||||
group nogroup
|
group nogroup
|
||||||
|
|
||||||
<% if float %>
|
<% if remote == "float" -%>
|
||||||
float
|
float
|
||||||
port <%= lport %>
|
port <%= lport %>
|
||||||
<% else %>
|
<% else -%>
|
||||||
remote <%= remote %>
|
remote <%= remote %>
|
||||||
rport <%= rport %>
|
rport <%= rport %>
|
||||||
lport <%= lport %>
|
lport <%= lport %>
|
||||||
<% end %>
|
<% end -%>
|
||||||
|
|
||||||
ifconfig <%= own_v4_tunnel %> <%= v4_tunnel %>
|
ifconfig <%= own_v4_tunnel %> <%= v4_tunnel %>
|
||||||
secret /etc/openvpn/<%= name %>.key
|
secret /etc/openvpn/<%= name %>.key
|
||||||
|
Loading…
Reference in New Issue
Block a user