You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
4.7 KiB
190 lines
4.7 KiB
require "optparse" |
|
require "shellwords" |
|
require_relative "utils.rb" |
|
|
|
ROOT_PATH = Pathname.new(File.expand_path("../../.." ,__FILE__)) |
|
EZJAIL_CONFIG_PATH = Pathname.new("/usr/local/etc/ezjail/") |
|
|
|
class Jail |
|
def initialize(name, properties={}) |
|
@name = name |
|
@properties = properties |
|
end |
|
attr_accessor :name, :properties |
|
|
|
def ip4 |
|
extract_ip("ip4") |
|
end |
|
def ip6 |
|
extract_ip("ip6") |
|
end |
|
|
|
private |
|
def extract_ip(type) |
|
ips = @properties[type] || [] |
|
ips.map do |addr| |
|
# example: em0|192.168.67.0 -> 192.168.67.0 |
|
addr =~ /\|?([^|]+)$/ |
|
$1 |
|
end |
|
end |
|
|
|
def pointers(&block) |
|
subnet_arpa = subnet.arpa |
|
version = subnet.version |
|
|
|
data["network"].each do |name, data| |
|
next unless data["ipv#{version}"] |
|
arpa = NetAddr::CIDR.create(data["ipv#{version}"]).arpa |
|
addr = arpa[0, arpa.size - subnet_arpa.size - 1] |
|
yield addr, name |
|
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 settings["flavour"] |
|
flavour = ["-f", settings["flavour"]] |
|
end |
|
sh("ezjail-admin", "create", *flavour, name, ipconfig) |
|
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_jail_config |
|
jails.each do |jail| |
|
jail_data = default_jail_conf.merge(jail.properties) |
|
templ = Template.new(ROOT_PATH.join("templates/jail.erb")) |
|
properties = jail_properties(jail.name, jail_data) |
|
conf_path = EZJAIL_CONFIG_PATH.join(jail.name) |
|
atomic_write(conf_path, templ.render(name: jail.name, |
|
properties: properties, |
|
provide: jail_data["provide"], |
|
require_: jail_data["require"], |
|
before: jail_data["before"])) |
|
end |
|
end |
|
|
|
def update_fstabs |
|
templ = Template.new(ROOT_PATH.join("templates/fstab.erb")) |
|
jails.each do |jail| |
|
fstab = settings["fstab"].dup |
|
fstab.concat(jail.properties["fstab"] || []) |
|
fstab.map! do |entry| |
|
entry % { name: jail.name } |
|
end |
|
|
|
path = "/etc/fstab.#{jail.name}" |
|
atomic_write(path, templ.render(fstab: fstab)) |
|
end |
|
end |
|
|
|
def update_zone |
|
end |
|
|
|
def update_rzone |
|
end |
|
|
|
private |
|
def settings |
|
{ |
|
"ip4_subnet" => "192.168.10.0/24", |
|
"ip6_subnet" => "fd7d:aed0:18aa::/48", |
|
"fstab" => [ |
|
"/usr/jails/basejail /usr/jails/%{name}/basejail nullfs ro 0 0", |
|
], |
|
}.merge(data["settings"]) |
|
end |
|
|
|
def default_jail_conf |
|
{ |
|
"provide" => "standard_ezjail", |
|
"require" => [], |
|
"before" => [], |
|
"exec_start" => "/bin/sh /etc/rc", |
|
"exec_stop" => nil, |
|
"hostname" => "%{name}", |
|
"rootdir" => "/usr/jails/%{name}", |
|
"mount_enable" => true, |
|
"devfs_ruleset" => "devfsrules_jails", |
|
"procfs_enable" => true, |
|
"fdescfs_enable" => true, |
|
"image" => nil, |
|
"imagetype" => nil, |
|
"attachparams" => nil, |
|
"attachblocking" => nil, |
|
"forceblocking" => nil, |
|
"zfs_datasets" => nil, |
|
"cpuset" => nil, |
|
"fib" => nil, |
|
"parentzfs" => nil, |
|
"parameters" => nil, |
|
"post_start_script" => nil, |
|
"retention_policy" => nil |
|
}.merge(data["default_jail_conf"]) |
|
end |
|
|
|
def jail_properties(name, properties) |
|
props = properties.dup |
|
|
|
ips = [] |
|
ips.concat(props.delete("ip4") || []) |
|
ips.concat(props.delete("ip6") || []) |
|
unless ips.empty? |
|
props["ip"] = ips.join(",") |
|
end |
|
props.each do |prop, value| |
|
props[prop] = serialize_property(name, value) |
|
end |
|
|
|
props |
|
end |
|
|
|
def serialize_property(name, value) |
|
str = case value |
|
when TrueClass |
|
return value ? "YES" : "NO" |
|
when String |
|
value % { name: name } |
|
else |
|
value |
|
end |
|
Shellwords.escape(str) |
|
end |
|
|
|
def jails |
|
data["jails"].map do |name, properties| |
|
Jail.new(name, properties) |
|
end |
|
end |
|
|
|
def next_address(type) |
|
subnets = [] |
|
data["jails"].each do |k,v| |
|
if v[type].is_a? Array |
|
v[type].each do |subnet| |
|
subnets << NetAddr::CIDR.create(subnet) |
|
end |
|
end |
|
end |
|
subnet = settings["#{type}_subnet"] |
|
next_free_subnet(NetAddr::CIDR.create(subnet), subnets) |
|
end |
|
end
|
|
|