jails-management/scripts/jail

157 lines
3.6 KiB
Ruby
Executable File

#!/usr/bin/env ruby
require "optparse"
require "shellwords"
require_relative "utils"
OPTION_PARSER = OptionParser.new do |opts|
opts.banner = "Usage: jails [options] [subcommand [options]]"
opts.separator ""
opts.separator <<HELP
Available subcommands:
add [options] NAME [ipaddress[,ipaddress,...]]: add jail
regenerate: regenerate configuration
See 'jail COMMAND --help' for more information on a specific command.
HELP
end
def usage
puts(OPTION_PARSER.help)
exit(0)
end
ROOT_PATH = Pathname.new(File.expand_path("../.." ,__FILE__))
EZJAIL_CONFIG_PATH = Pathname.new("/usr/local/etc/ezjail/")
DEFAULT_IP4_SUBNET = "192.168.10.0/24"
DEFAULT_IP6_SUBNET = "fd7d:aed0:18aa::/48"
class Jail
def initialize(name, properties={})
@name = name
@properties = properties
end
attr_accessor :name
def ip4; extract_ip["ip4"]; end
def ip6; extract_ip["ip6"]; end
private
def extract_ip(type)
ips = @properties[ip] || []
ips.map do |addr| extract_ip(addr)
# example: em0|192.168.67.0 -> 192.168.67.0
spec =~ /\|?([^|]+)$/
$1
end
end
end
class JailRegistry < Registry
def add(name)
data["jails"] ||= {}
data["settings"] ||= {}
ip4 = next_address("ip4")
ip6 = next_address("ip6")
data["jails"][name] = {
"ip4" => ip4,
"ip6" => ip6
}
ipconfig = "#{ip4},#{ip6}"
flavour = []
if data["settings"]["flavour"]
flavour = ["-f", data["settings"]["flavour"]]
end
sh("ezjail-admin", "create", *flavour, name, ipconfig)
end
def env(name)
jail_data = data[name] or die("no jail with name #{name} found")
templ = Template.new(ROOT_PATH.join("templates/jail.erb"))
puts(templ.render(name: name, properties: jail_properties(jail_data)))
end
def update_pf_vars
templ = Template.new(ROOT_PATH.join("templates/pf.erb"))
path = ROOT_PATH.join("pf_vars.conf")
atomic_write(path, templ.render(jails: jails))
end
def update_config_symlinks
conf_path = ROOT_PATH.join("scripts/jail_conf")
jails.each do |name|
path = EZJAIL_CONFIG_PATH.join(name)
FileUtils.ln_sf(conf_path, path)
end
end
def update_fstabs
templ = Template.new(ROOT_PATH.join("templates/fstab.erb"))
atomic_write(path, templ.render(jails: jails))
end
private
def jail_properties(name)
props = @properties.dup
ips = props.delete("ip4") || []
ips.concat(props.delete("ip6") || [])
if props["ip4"] || props["ip6"]
props["ip"] = ips.join(",")
end
props.each do |prop, value|
props[prop] = Shellwords.escape(value)
end
props
end
def jails
jails = {}
data["jails"].each do |name, properties|
jails[name] = Jail.new(name, properties)
end
jails
end
def next_address(type)
assigned_subnets = data["jails"].map do |k,v|
NetAddr::CIDR.create(v[type]) if v[type]
end.compact
subnet = data["settings"]["#{type}_subnet"]
default = { "ip4" => DEFAULT_IP4_SUBNET, "ip6" => DEFAULT_IP6_SUBNET }
subnet ||= default[type]
next_free_subnet(NetAddr::CIDR.create(subnet), assigned_subnets)
end
end
def main(args)
OPTION_PARSER.order!(args)
usage if args.size < 1
registry = JailRegistry.new
case command = args.shift
when "add"
name = args.first or die "name required"
registry.add(name)
registry.save
when nil
usage
when "regenerate"
when "env"
name = args.first or die "name required"
registry.env(name)
else
die "unknown subcommand #{command}"
end
registry.update_pf_vars
registry.update_fstabs
registry.update_config_symlinks
end
main(ARGV)