outsource in library, rework jails env

This commit is contained in:
Jörg Thalheim 2015-01-23 21:42:48 +00:00
parent de798d18cc
commit b40eed619a
8 changed files with 253 additions and 180 deletions

View File

@ -1,5 +1,13 @@
{ {
"settings": { "settings": {
"soa": "ns1.higgsboson.tk.",
"serial": 114,
"refresh": "1H",
"hostmaster": "hostmaster.higgsboson.tk",
"domain": "eva.higgsboson.tk",
"retry": "4H",
"expire": "3W",
"minimum": "1D",
"ip4_subnet": "192.168.67.0/24", "ip4_subnet": "192.168.67.0/24",
"ip6_subnet": "2a03:b0c0:2:d0:1::/80", "ip6_subnet": "2a03:b0c0:2:d0:1::/80",
"flavor": "default", "flavor": "default",

View File

@ -1,7 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
require "optparse" require_relative "lib/utils.rb"
require "shellwords" require_relative "lib/jail.rb"
require_relative "utils"
OPTION_PARSER = OptionParser.new do |opts| OPTION_PARSER = OptionParser.new do |opts|
opts.banner = "Usage: jails [options] [subcommand [options]]" opts.banner = "Usage: jails [options] [subcommand [options]]"
@ -20,173 +19,6 @@ def usage
exit(0) exit(0)
end end
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
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 env(name)
jail_data = data["jails"][name] or die("no jail with name #{name} found")
jail_data = default_jail_conf.merge(jail_data)
templ = Template.new(ROOT_PATH.join("templates/jail_env.erb"))
properties = jail_properties(name, jail_data)
puts(templ.render(name: name, properties: properties))
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")
FileUtils.mkdir_p(EZJAIL_CONFIG_PATH)
jails.each do |jail|
FileUtils.ln_sf(conf_path, EZJAIL_CONFIG_PATH.join(jail.name))
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
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
{
"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 = 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
def main(args) def main(args)
OPTION_PARSER.order!(args) OPTION_PARSER.order!(args)
@ -198,19 +30,18 @@ def main(args)
when "add" when "add"
name = args.first or die "name required" name = args.first or die "name required"
registry.add(name) registry.add(name)
registry.save
when nil when nil
usage usage
when "regenerate" when "regenerate"
when "env"
name = args.first or die "name required"
registry.env(name)
else else
die "unknown subcommand #{command}" die "unknown subcommand #{command}"
end end
registry.update_pf_vars registry.update_pf_vars
registry.update_fstabs registry.update_fstabs
registry.update_config_symlinks registry.update_jail_config
registry.update_zone
registry.update_rzone
registry.save
end end
main(ARGV) main(ARGV)

View File

@ -1,4 +0,0 @@
#!/bin/sh
echo "TODO"
exit 1

183
scripts/lib/jail.rb Normal file
View File

@ -0,0 +1,183 @@
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))
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
{
"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

View File

@ -7,7 +7,7 @@ require "pry"
require "netaddr" require "netaddr"
class Registry class Registry
PATH = Pathname.new(File.expand_path("../../jails.json", __FILE__)) PATH = Pathname.new(File.expand_path("../../../jails.json", __FILE__))
def initialize def initialize
@data = JSON.load(File.open(Registry::PATH)) @data = JSON.load(File.open(Registry::PATH))
end end

View File

@ -1,3 +1,4 @@
# THIS FILE WAS GENERATED, CHANGES WILL BE OVERWRITTEN
<% properties.each do |property,value| -%> <% properties.each do |property,value| -%>
export jail_<%= name %>_<%= property %>=<%= value %> export jail_<%= name %>_<%= property %>=<%= value %>
<% end -%> <% end -%>

26
templates/rzone.erb Normal file
View File

@ -0,0 +1,26 @@
@ IN SOA <%= data["zone"]["soa"] %> <%= data["zone"]["hostmaster"] %> (
<%= data["zone"]["serial"] %> ; serial
<%= data["zone"]["refresh"] %> ; refresh
<%= data["zone"]["retry"] %> ; retry
<%= data["zone"]["expire"] %> ; expire
<%= data["zone"]["minimum"] %>) ; minimum
<% data["jails"].each do |name, value| -%>
<% if value["ns"] -%>
IN NS <%= name %>
<% end -%>
<% end -%>
<% data["jails"].each do |name, value| -%>
<% if value["ns"] -%>
<% if value["ipv4"] -%>
<%= name %> A <%= value["ipv4"] %>
<% end -%>
<% if value["ipv6"] -%>
<%= name %> AAAA <%= value["ipv6"] %>
<% end -%>
<% end -%>
<% end -%>
<% pointers do |addr, name| -%>
<%= addr %> PTR <%= name %>.<%= data["zone"]["domain"] %>.
<% end -%>

28
templates/zone.erb Normal file
View File

@ -0,0 +1,28 @@
@ IN SOA <%= data["zone"]["soa"] %> <%= data["zone"]["hostmaster"] %> (
<%= data["zone"]["serial"] %> ; serial
<%= data["zone"]["refresh"] %> ; refresh
<%= data["zone"]["retry"] %> ; retry
<%= data["zone"]["expire"] %> ; expire
<%= data["zone"]["minimum"] %>) ; minimum
<% data["jails"].each do |name, value| -%>
<% if value["ns"] -%>
IN NS <%= name %>
<% end -%>
<% end -%>
<% jails.each do |jail| %>
<% jail.cname -%>
<%= name %> CNAME <%= jail.cname %>
<% end -%>
<% jail.srv.each do |srv| -%>
<%= name %> SRV <%= srv %>
<% end -%>
<% jail.ip4.each do |ip| -%>
<%= name %> A <%= ip %>
ipv4.<%= name %> A <%= ip %>
<% end -%>
<% if value["ipv6"] -%>
<%= name %> AAAA <%= ip(value["ipv6"]) %>
ipv6.<%= name %> AAAA <%= ip(value["ipv6"]) %>
<% end -%>
<% end -%>