wip
This commit is contained in:
commit
aba9bb87d1
47
jails.json
Normal file
47
jails.json
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"settings": {
|
||||||
|
"ip4_subnet": "192.168.67.0/24",
|
||||||
|
"ip6_subnet": "2a03:b0c0:2:d0:1::/80",
|
||||||
|
"flavor": "default"
|
||||||
|
},
|
||||||
|
"default_jail_conf": {
|
||||||
|
"fstab": [
|
||||||
|
"/usr/jails/basejail /usr/jails/%{name}/basejail nullfs ro 0 0",
|
||||||
|
"/data/pkg /usr/jails/%{name} nullfs ro 0 0"
|
||||||
|
]
|
||||||
|
"exec_start": "/bin/sh /etc/rc",
|
||||||
|
"exec_stop": null,
|
||||||
|
"hostname": "%{name}",
|
||||||
|
"rootdir": "/usr/jails/%{name}",
|
||||||
|
"mount_enable": true,
|
||||||
|
"devfs_ruleset": "devfsrules_jails",
|
||||||
|
"procfs_enable": true,
|
||||||
|
"fdescfs_enable": true,
|
||||||
|
"image": null,
|
||||||
|
"imagetype": null,
|
||||||
|
"attachparams": null,
|
||||||
|
"attachblocking": null,
|
||||||
|
"forceblocking": null,
|
||||||
|
"zfs_datasets": null,
|
||||||
|
"cpuset": null,
|
||||||
|
"fib": null,
|
||||||
|
"parentzfs": null,
|
||||||
|
"parameters": null,
|
||||||
|
"post_start_script": null,
|
||||||
|
"retention_policy": null
|
||||||
|
},
|
||||||
|
"jails": {
|
||||||
|
"dns": {
|
||||||
|
"ip4": ["192.168.67.2"],
|
||||||
|
"ip6": ["2a03:b0c0:2:d0::2a5:f002/128"]
|
||||||
|
},
|
||||||
|
"dn42": {
|
||||||
|
"ip4": ["192.168.67.4"],
|
||||||
|
"ip6": ["2a03:b0c0:2:d0:1::3"]
|
||||||
|
},
|
||||||
|
"mail": {
|
||||||
|
"ip4": ["192.168.67.1"],
|
||||||
|
"ip6": ["2a03:b0c0:2:d0:1::1"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
pf_vars.conf
Normal file
6
pf_vars.conf
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
ipv4_dns="192.168.67.2"
|
||||||
|
ipv6_dns="2a03:b0c0:2:d0::2a5:f002/128"
|
||||||
|
ipv4_dn42="192.168.67.4"
|
||||||
|
ipv6_dn42="2a03:b0c0:2:d0:1::3"
|
||||||
|
ipv4_mail="192.168.67.1"
|
||||||
|
ipv6_mail="2a03:b0c0:2:d0:1::1"
|
156
scripts/jail
Executable file
156
scripts/jail
Executable file
@ -0,0 +1,156 @@
|
|||||||
|
#!/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)
|
65
scripts/utils.rb
Normal file
65
scripts/utils.rb
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
require "ostruct"
|
||||||
|
require "fileutils"
|
||||||
|
require "erb"
|
||||||
|
require "json"
|
||||||
|
require "pathname"
|
||||||
|
require "pry"
|
||||||
|
require "netaddr"
|
||||||
|
|
||||||
|
class Registry
|
||||||
|
PATH = Pathname.new(File.expand_path("../../jails.json", __FILE__))
|
||||||
|
def initialize
|
||||||
|
@data = JSON.load(File.open(Registry::PATH))
|
||||||
|
end
|
||||||
|
attr_accessor :data
|
||||||
|
def save
|
||||||
|
f = File.open(Registry::PATH, "w+")
|
||||||
|
f.puts JSON.pretty_generate(@data)
|
||||||
|
f.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def atomic_write(path, content)
|
||||||
|
dir = File.dirname(path)
|
||||||
|
unless Dir.exist?(dir)
|
||||||
|
FileUtils.mkdir_p(dir)
|
||||||
|
end
|
||||||
|
temp_path = path.to_s + ".tmp"
|
||||||
|
File.open(temp_path, 'w+') do |f|
|
||||||
|
f.write(content)
|
||||||
|
end
|
||||||
|
|
||||||
|
FileUtils.mv(temp_path, path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def sh(cmd, *args)
|
||||||
|
puts "$ #{cmd} "+ args.map {|a| "'#{a}'" }.join(" ")
|
||||||
|
system(cmd, *args)
|
||||||
|
end
|
||||||
|
|
||||||
|
def die(msg)
|
||||||
|
$stderr.puts(msg)
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_free_subnet(subnet, assigned_subnets)
|
||||||
|
subnet.enumerate(Limit: 1E4, Short: true)[1..1E4].each do |cidr|
|
||||||
|
assigned = assigned_subnets.find { |s| s.contains?(cidr) || s == cidr }
|
||||||
|
return cidr unless assigned
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class TemplateContext < OpenStruct
|
||||||
|
def get_binding
|
||||||
|
binding
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Template
|
||||||
|
def initialize(path)
|
||||||
|
@erb = ERB.new(File.read(path), nil, '-')
|
||||||
|
end
|
||||||
|
def render(params={})
|
||||||
|
@erb.result(TemplateContext.new(params).get_binding)
|
||||||
|
end
|
||||||
|
end
|
4
templates/fstab.erb
Normal file
4
templates/fstab.erb
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# THIS FILE WAS GENERATED, CHANGES WILL BE OVERWRITTEN
|
||||||
|
<% fstab.each do |entry| -%>
|
||||||
|
<%= entry %>
|
||||||
|
<% end -%>
|
3
templates/jail_env.erb
Normal file
3
templates/jail_env.erb
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<% jail_properties.each do |property,value| -%>
|
||||||
|
export jail_<%= name %>_<%= property %>=<%= value %>
|
||||||
|
<% end -%>
|
14
templates/pf.erb
Normal file
14
templates/pf.erb
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# THIS FILE WAS GENERATED, CHANGES WILL BE OVERWRITTEN
|
||||||
|
<% jails.each do |jail| -%>
|
||||||
|
|
||||||
|
<%= name %>_ip4="{<%= jail.ip4.join(", ") %>}"
|
||||||
|
<% jail.ip4.each_with_index |ip, idx| -%>
|
||||||
|
<%= name %>_ip4_<%= idx %>="<%= ip %>"
|
||||||
|
<% end -%>
|
||||||
|
|
||||||
|
<%= name %>_ip6="{<%= jail.ip6.join(", ") %>}"
|
||||||
|
<% jail.ip6.each_with_index |ip, idx| -%>
|
||||||
|
<%= name %>_ip6_<%= idx %>="<%= ip %>"
|
||||||
|
<% end -%>
|
||||||
|
|
||||||
|
<% end -%>
|
Loading…
Reference in New Issue
Block a user