first commit

This commit is contained in:
root 2014-08-18 10:29:43 +02:00
commit e3354dfe8e
16 changed files with 708 additions and 0 deletions

170
container.json Normal file
View File

@ -0,0 +1,170 @@
{
"zone": {
"soa": "higgsboson.tk.",
"ns": "higgsboson.tk.",
"serial": 46,
"refresh": "1H",
"retry": "4H",
"expire": "3W",
"minimum": "1D",
"v4_subnet": "10.100.0.0/16",
"v6_subnet": "2a01:4f8:210:31fd:1::/80"
},
"network": {
"bridge": {
"ipv4": "10.100.0.1/32",
"ipv6": "2a01:4f8:210:31fd:1::1/128",
"lxc": false
},
"base": {
"ipv4": "10.100.0.2/32",
"ipv6": "2a01:4f8:210:31fd:1::2/128"
},
"ldap": {
"ipv4": "10.100.0.3/32",
"ipv6": "2a01:4f8:210:31fd:1::3/128",
"group": "php"
},
"web": {
"ipv4": "10.100.0.5/32",
"ipv6": "2a01:4f8:210:31fd:1::5/128"
},
"dns": {
"ipv4": "10.100.0.6/32",
"ipv6": "2a01:4f8:210:31fd:1::6/128"
},
"faces": {
"ipv4": "10.100.0.7/32",
"ipv6": "2a01:4f8:210:31fd:1::7/128"
},
"jtes": {
"ipv4": "10.100.0.8/32",
"ipv6": "2a01:4f8:210:31fd:1::8/128"
},
"mysql": {
"ipv4": "10.100.0.9/32",
"ipv6": "2a01:4f8:210:31fd:1::9/128"
},
"git": {
"ipv4": "10.100.0.4/32",
"ipv6": "2a01:4f8:210:31fd:1::4/128"
},
"postgres": {
"ipv4": "10.100.0.10/32",
"ipv6": "2a01:4f8:210:31fd:1::a/128"
},
"phpmyadmin": {
"ipv4": "10.100.0.11/32",
"ipv6": "2a01:4f8:210:31fd:1::b/128",
"group": "php",
"vars": {
"php_extensions": [
"mysql",
"pdo_mysql"
]
}
},
"phppgadmin": {
"ipv4": "10.100.0.13/32",
"ipv6": "2a01:4f8:210:31fd:1::d/128",
"group": "php",
"vars": {
"php_extensions": [
"pgsql",
"pdo_pgsql"
]
}
},
"adminer": {
"ipv4": "10.100.0.14/32",
"ipv6": "2a01:4f8:210:31fd:1::e/128",
"group": "php",
"vars": {
"php_extensions": [
"mysql",
"pdo_mysql",
"pgsql",
"pdo_pgsql"
]
}
},
"mail": {
"ipv4": "10.100.0.16/32",
"ipv6": "2a01:4f8:210:31fd:1::10/128"
},
"istwiki": {
"ipv4": "10.100.0.17/32",
"ipv6": "2a01:4f8:210:31fd:1::11/128",
"group": "php",
"vars": {
"php_extensions": [
"mysql",
"pdo_mysql"
]
}
},
"ytm": {
"ipv4": "10.100.0.18/32",
"ipv6": "2a01:4f8:210:31fd:1::12/128",
"group": "php",
"vars": {
"php_extensions": [
"mysql",
"mysqli",
"pdo_mysql"
]
}
},
"ldapadmin": {
"ipv4": "10.100.0.12/32",
"ipv6": "2a01:4f8:210:31fd:1::c/128",
"group": "php",
"vars": {
"php_extensions": [
"ldap"
]
}
},
"rainloop": {
"ipv4": "10.100.0.19/32",
"ipv6": "2a01:4f8:210:31fd:1::13/128",
"group": "php",
"vars": {
"php_extensions": [
"pgsql",
"pdo_pgsql",
"openssl"
]
}
},
"owncloud": {
"ipv4": "10.100.0.15/32",
"ipv6": "2a01:4f8:210:31fd:1::f/128",
"group": "php",
"vars": {
"php_extensions": [
"pgsql",
"pdo_pgsql",
"gd",
"iconv",
"xmlrpc",
"zip",
"bz2",
"intl",
"mcrypt",
"openssl",
"ldap",
"apcu",
"exif",
"imagick"
]
}
},
"test": {
"ipv4": "10.100.0.20/32",
"ipv6": "2a01:4f8:210:31fd:1::14/128",
"group": null,
"vars": null
}
}
}

22
default.conf Normal file
View File

@ -0,0 +1,22 @@
lxc.autodev = 1
lxc.kmsg = 0
lxc.cap.drop = sys_module mac_admin mac_override sys_time net_admin
lxc.network.type = veth
lxc.network.link = br0
lxc.network.flags = up
lxc.network.name =eth0
lxc.network.mtu = 1500
lxc.network.ipv4.gateway = auto
lxc.network.ipv6.gateway = 2a01:4f8:210:31fd:1::1
lxc.mount.entry = /data/pacman-pkg-cache var/cache/pacman/pkg none bind 0 0
lxc.mount.entry = /data/repo srv/repo none bind,ro 0 0
lxc.mount.entry = /run/systemd/journal mnt/journal none bind,ro,create=dir 0 0
lxc.hook.clone = /etc/lxc/hooks/setup-machine-id
lxc.hook.clone = /etc/lxc/hooks/remove-journal
lxc.hook.clone = /etc/lxc/hooks/cleanup-lxc-config
lxc.hook.clone = /etc/lxc/hooks/create-lxc-config
lxc.hook.clone = /etc/lxc/hooks/update-zone
lxc.hook.clone = /etc/lxc/hooks/ansible

11
hooks/ansible Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
set -x
if [ -z "$LXC_NAME" ]; then
echo "LXC_NAME not set" 2>&1
exit 1
fi
lxc-start -n "$LXC_NAME" -d
ansible-playbook -i /etc/ansible/inventory /etc/ansible/site.yml --limit "$LXC_NAME" 1>&2
lxc-stop -n "$LXC_NAME"

27
hooks/cleanup-lxc-config Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/env ruby
require 'json'
require 'fileutils'
abort "Must run as root" unless Process.uid == 0
CONTAINER_DATA = "/etc/lxc/container.json"
data = JSON.load(File.open(CONTAINER_DATA))
containers = `lxc-ls -1`.split(/\n/)
modified = false
data["network"].each do |host, value|
unless containers.include?(host) or value["lxc"] == false
data["network"].delete(host)
modified = true
end
end
if modified
FileUtils.cp(CONTAINER_DATA, CONTAINER_DATA + ".backup")
File.open(CONTAINER_DATA, "w+") do |f|
f.puts JSON.pretty_generate(data)
end
else
puts "Unchanged"
end

124
hooks/create-lxc-config Executable file
View File

@ -0,0 +1,124 @@
#!/usr/bin/env ruby
require "erb"
require "pathname"
require 'ostruct'
require 'optparse'
require 'json'
require 'netaddr'
def try_env(key)
ENV[key] or abort("enviroment variable '#{key}' not set")
end
def address_free?(assigned_subnets, address)
assigned_subnets.find { |s| s.contains?(address) || s == address }
end
def find_address(subnet, assigned_subsubnets)
subnet.enumerate(Limit: 1E4, Short: true)[1..1E4].each do |cidr|
unless address_free?(assigned_subsubnets, cidr)
return cidr
end
end
end
CONFIG_PATH = Pathname.new("/etc/lxc/")
IPV4_SUBNET = NetAddr::CIDR.create("10.100.0.0/16")
IPV6_SUBNET = NetAddr::CIDR.create("2a01:4f8:210:31fd:1::/80")
options = OpenStruct.new
options.container_name = try_env("LXC_NAME")
options.container_config = try_env("LXC_CONFIG_FILE")
options.rootfs = try_env("LXC_ROOTFS_PATH")
OptionParser.new do |opts|
opts.banner = "Usage: create-lxc-config [options]"
opts.on("-4", "--ipv4", "private Ipv4 subnet") do |v|
options.ipv4 = v
end
opts.on("-6", "--ipv6", "public Ipv6 subnet") do |v|
options.ipv6 = v
end
opts.on("--group GROUP", String, "set ansible group (default NONE)") do |group|
options.group = group
end
opts.on("--vars FILE", String, "set json file for ansible variables") do |vars|
begin
options.vars = JSON.load(File.open(vars))
unless options.vars.is_a? Hash
abort "vars: Should be a json object"
end
rescue SystemCallError => e
abort "Failed to open '#{vars}': #{e.message}"
rescue JSON::ParserError => e
abort "Failed to parse ansible variables: #{e.message}"
end
end
end.parse!
container_data = CONFIG_PATH.join("container.json")
data = if File.exists?(container_data)
JSON.load(File.open(container_data))
else
{}
end
data["network"] ||= {}
#if data["network"][options.container_name]
# abort "container name '#{options.container_name}' in '#{container_data}' already in use!"
#end
data["network"][options.container_name] = {}
ipv4_subnets = data["network"].map {|k,v| NetAddr::CIDR.create(v["ipv4"]) if v["ipv4"]}.compact!
ipv6_subnets = data["network"].map {|k,v| NetAddr::CIDR.create(v["ipv6"]) if v["ipv6"]}.compact!
ipv4_address = if options.ipv4.nil?
find_address(IPV4_SUBNET, ipv4_subnets)
elsif address_free?(ipv4_subnets, options.ipv4)
abort "The address #{ipv4} is already assigned"
else
options.ipv4
end
ipv6_address = if options.ipv6.nil?
find_address(IPV6_SUBNET, ipv6_subnets)
elsif address_free?(ipv6_subnets, options.ipv6)
abort "The address #{ipv4} is already assigned"
else
options.ipv6
end
network_config = data["network"][options.container_name]
network_config["ipv4"] = NetAddr::CIDR.create(ipv4_address).to_s(Short: true)
network_config["ipv6"] = NetAddr::CIDR.create(ipv6_address).to_s(Short: true)
network_config["group"] = options.group
network_config["vars"] = options.vars
open(container_data, File::CREAT|File::TRUNC|File::RDWR) do |f|
f.write(JSON.pretty_generate(data))
end
class TemplateContext
def initialize(hash)
hash.each do |key, value|
singleton_class.send(:define_method, key) { value }
end
end
def get_binding
binding
end
end
context = TemplateContext.new(container_name: options.container_name,
ipv4: NetAddr::CIDR.create(ipv4_address, Mask: IPV4_SUBNET.to_i(:netmask)).desc(IP: true, Short: true),
ipv6: NetAddr::CIDR.create(ipv6_address, Mask: IPV6_SUBNET.to_i(:netmask)).desc(IP: true, Short: true),
rootfs: options.rootfs)
erb = ERB.new(File.read(CONFIG_PATH.join("templates", "config.erb")))
open(options.container_config, "w+") do |f|
f.write(erb.result(context.get_binding))
end

10
hooks/remove-journal Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
set -xeu
JOURNAL="$LXC_ROOTFS_PATH/var/log/journal"
if [ -d "$JOURNAL" ]; then
cd $JOURNAL
rm -rf *
fi

11
hooks/setup-machine-id Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
set -eux
MACHINE_ID="${LXC_ROOTFS_PATH}/etc/machine-id"
if [ -f "$MACHINE_ID" ]; then
rm "$MACHINE_ID"
fi
systemd-machine-id-setup --root="$LXC_ROOTFS_PATH"

4
hooks/update-container Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
pacman -r "$LXC_ROOTFS_PATH" -Syu --noconfirm
exit $?

85
hooks/update-zone Executable file
View File

@ -0,0 +1,85 @@
#!/usr/bin/env ruby
require 'json'
require 'erb'
require 'netaddr'
require 'fileutils'
require 'pathname'
LXC_ROOT = Pathname.new("/etc/lxc")
ZONE_PATH = LXC_ROOT.join("zones")
TEMPLATE_PATH = LXC_ROOT.join("templates")
CONTAINER_DATA = LXC_ROOT.join("container.json")
LXC_ZONE = ZONE_PATH.join("lxc.zone")
DNS_CONTAINER = "dns"
def atomic_write(path, content)
temp_path = path.to_s + ".tmp"
File.open(temp_path, 'w+') do |f|
f.write(content)
end
FileUtils.mv(temp_path, path)
end
class ZoneData
def initialize(data)
@data = data
end
attr_reader :data
def get_binding
binding
end
def ip(subnet)
NetAddr::CIDR.create(subnet).ip(Short: true)
end
end
class RdnsData
def initialize(data, subnet)
@data = data
@subnet = subnet
end
attr_reader :data, :subnet
def get_binding
binding
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
json = JSON.load(File.open(CONTAINER_DATA))
json["zone"]["serial"] += 1
rdns_zone_template = File.read(TEMPLATE_PATH.join("rdns-zone.erb"))
zone_data = ZoneData.new(json)
lxc_zone_template = File.read(TEMPLATE_PATH.join("lxc-zone.erb"))
zone = ERB.new(lxc_zone_template, nil, '-').result(zone_data.get_binding)
v4_subnet = NetAddr::CIDR.create(json["zone"]["v4_subnet"])
v4_rdns_path = ZONE_PATH.join(v4_subnet.arpa.gsub(/\.$/, ""))
v4_rdns_zone = ERB.new(rdns_zone_template, nil, '-').result(RdnsData.new(json, v4_subnet).get_binding)
v6_subnet = NetAddr::CIDR.create(json["zone"]["v6_subnet"])
v6_rdns_path = ZONE_PATH.join(v6_subnet.arpa.gsub(/\.$/, ""))
v6_rdns_zone = ERB.new(rdns_zone_template, nil, '-').result(RdnsData.new(json, v6_subnet).get_binding)
atomic_write(LXC_ZONE, zone)
atomic_write(v4_rdns_path, v4_rdns_zone)
atomic_write(v6_rdns_path, v6_rdns_zone)
atomic_write(CONTAINER_DATA, JSON.pretty_generate(json))
system("lxc-attach", "-n", DNS_CONTAINER, "--", "rndc", "reload")

2
lxc.conf Normal file
View File

@ -0,0 +1,2 @@
lxc.lxcpath = /data/containers
lxc.bdev.zfs.root = data/containers

6
templates/config.erb Normal file
View File

@ -0,0 +1,6 @@
lxc.include = /etc/lxc/default.conf
lxc.utsname = <%= container_name %>
lxc.rootfs = <%= rootfs %>
lxc.network.ipv4 = <%= ipv4 %>
lxc.network.ipv6 = <%= ipv6 %>
lxc.network.veth.pair = lxc_<%= container_name[0..(16-4)] %>

21
templates/lxc-zone.erb Normal file
View File

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

11
templates/rdns-zone.erb Normal file
View File

@ -0,0 +1,11 @@
@ IN SOA <%= data["zone"]["soa"] %> hostmaster (
<%= data["zone"]["serial"] %> ; serial
<%= data["zone"]["refresh"] %> ; refresh
<%= data["zone"]["retry"] %> ; retry
<%= data["zone"]["expire"] %> ; expire
<%= data["zone"]["minimum"] %>) ; minimum
NS <%= data["zone"]["ns"] %>
<% pointers do |addr, name| %>
<%= addr %> PTR <%= name %>.lxc.
<% end -%>

View File

@ -0,0 +1,48 @@
@ IN SOA higgsboson.tk. hostmaster (
46 ; serial
1H ; refresh
4H ; retry
3W ; expire
1D) ; minimum
NS higgsboson.tk.
1.0.0.0.0.0.0.0.0.0.0.0 PTR bridge.lxc.
2.0.0.0.0.0.0.0.0.0.0.0 PTR base.lxc.
3.0.0.0.0.0.0.0.0.0.0.0 PTR ldap.lxc.
5.0.0.0.0.0.0.0.0.0.0.0 PTR web.lxc.
6.0.0.0.0.0.0.0.0.0.0.0 PTR dns.lxc.
7.0.0.0.0.0.0.0.0.0.0.0 PTR faces.lxc.
8.0.0.0.0.0.0.0.0.0.0.0 PTR jtes.lxc.
9.0.0.0.0.0.0.0.0.0.0.0 PTR mysql.lxc.
4.0.0.0.0.0.0.0.0.0.0.0 PTR git.lxc.
a.0.0.0.0.0.0.0.0.0.0.0 PTR postgres.lxc.
b.0.0.0.0.0.0.0.0.0.0.0 PTR phpmyadmin.lxc.
d.0.0.0.0.0.0.0.0.0.0.0 PTR phppgadmin.lxc.
e.0.0.0.0.0.0.0.0.0.0.0 PTR adminer.lxc.
0.1.0.0.0.0.0.0.0.0.0.0 PTR mail.lxc.
1.1.0.0.0.0.0.0.0.0.0.0 PTR istwiki.lxc.
2.1.0.0.0.0.0.0.0.0.0.0 PTR ytm.lxc.
c.0.0.0.0.0.0.0.0.0.0.0 PTR ldapadmin.lxc.
3.1.0.0.0.0.0.0.0.0.0.0 PTR rainloop.lxc.
f.0.0.0.0.0.0.0.0.0.0.0 PTR owncloud.lxc.
4.1.0.0.0.0.0.0.0.0.0.0 PTR test.lxc.

48
zones/100.10.in-addr.arpa Normal file
View File

@ -0,0 +1,48 @@
@ IN SOA higgsboson.tk. hostmaster (
46 ; serial
1H ; refresh
4H ; retry
3W ; expire
1D) ; minimum
NS higgsboson.tk.
1.0 PTR bridge.lxc.
2.0 PTR base.lxc.
3.0 PTR ldap.lxc.
5.0 PTR web.lxc.
6.0 PTR dns.lxc.
7.0 PTR faces.lxc.
8.0 PTR jtes.lxc.
9.0 PTR mysql.lxc.
4.0 PTR git.lxc.
10.0 PTR postgres.lxc.
11.0 PTR phpmyadmin.lxc.
13.0 PTR phppgadmin.lxc.
14.0 PTR adminer.lxc.
16.0 PTR mail.lxc.
17.0 PTR istwiki.lxc.
18.0 PTR ytm.lxc.
12.0 PTR ldapadmin.lxc.
19.0 PTR rainloop.lxc.
15.0 PTR owncloud.lxc.
20.0 PTR test.lxc.

108
zones/lxc.zone Normal file
View File

@ -0,0 +1,108 @@
@ IN SOA higgsboson.tk. hostmaster (
46 ; serial
1H ; refresh
4H ; retry
3W ; expire
1D) ; minimum
NS higgsboson.tk.
bridge A 10.100.0.1
ipv4.bridge A 10.100.0.1
bridge AAAA 2a01:4f8:210:31fd:1::1
ipv6.bridge AAAA 2a01:4f8:210:31fd:1::1
base A 10.100.0.2
ipv4.base A 10.100.0.2
base AAAA 2a01:4f8:210:31fd:1::2
ipv6.base AAAA 2a01:4f8:210:31fd:1::2
ldap A 10.100.0.3
ipv4.ldap A 10.100.0.3
ldap AAAA 2a01:4f8:210:31fd:1::3
ipv6.ldap AAAA 2a01:4f8:210:31fd:1::3
web A 10.100.0.5
ipv4.web A 10.100.0.5
web AAAA 2a01:4f8:210:31fd:1::5
ipv6.web AAAA 2a01:4f8:210:31fd:1::5
dns A 10.100.0.6
ipv4.dns A 10.100.0.6
dns AAAA 2a01:4f8:210:31fd:1::6
ipv6.dns AAAA 2a01:4f8:210:31fd:1::6
faces A 10.100.0.7
ipv4.faces A 10.100.0.7
faces AAAA 2a01:4f8:210:31fd:1::7
ipv6.faces AAAA 2a01:4f8:210:31fd:1::7
jtes A 10.100.0.8
ipv4.jtes A 10.100.0.8
jtes AAAA 2a01:4f8:210:31fd:1::8
ipv6.jtes AAAA 2a01:4f8:210:31fd:1::8
mysql A 10.100.0.9
ipv4.mysql A 10.100.0.9
mysql AAAA 2a01:4f8:210:31fd:1::9
ipv6.mysql AAAA 2a01:4f8:210:31fd:1::9
git A 10.100.0.4
ipv4.git A 10.100.0.4
git AAAA 2a01:4f8:210:31fd:1::4
ipv6.git AAAA 2a01:4f8:210:31fd:1::4
postgres A 10.100.0.10
ipv4.postgres A 10.100.0.10
postgres AAAA 2a01:4f8:210:31fd:1::a
ipv6.postgres AAAA 2a01:4f8:210:31fd:1::a
phpmyadmin A 10.100.0.11
ipv4.phpmyadmin A 10.100.0.11
phpmyadmin AAAA 2a01:4f8:210:31fd:1::b
ipv6.phpmyadmin AAAA 2a01:4f8:210:31fd:1::b
phppgadmin A 10.100.0.13
ipv4.phppgadmin A 10.100.0.13
phppgadmin AAAA 2a01:4f8:210:31fd:1::d
ipv6.phppgadmin AAAA 2a01:4f8:210:31fd:1::d
adminer A 10.100.0.14
ipv4.adminer A 10.100.0.14
adminer AAAA 2a01:4f8:210:31fd:1::e
ipv6.adminer AAAA 2a01:4f8:210:31fd:1::e
mail A 10.100.0.16
ipv4.mail A 10.100.0.16
mail AAAA 2a01:4f8:210:31fd:1::10
ipv6.mail AAAA 2a01:4f8:210:31fd:1::10
istwiki A 10.100.0.17
ipv4.istwiki A 10.100.0.17
istwiki AAAA 2a01:4f8:210:31fd:1::11
ipv6.istwiki AAAA 2a01:4f8:210:31fd:1::11
ytm A 10.100.0.18
ipv4.ytm A 10.100.0.18
ytm AAAA 2a01:4f8:210:31fd:1::12
ipv6.ytm AAAA 2a01:4f8:210:31fd:1::12
ldapadmin A 10.100.0.12
ipv4.ldapadmin A 10.100.0.12
ldapadmin AAAA 2a01:4f8:210:31fd:1::c
ipv6.ldapadmin AAAA 2a01:4f8:210:31fd:1::c
rainloop A 10.100.0.19
ipv4.rainloop A 10.100.0.19
rainloop AAAA 2a01:4f8:210:31fd:1::13
ipv6.rainloop AAAA 2a01:4f8:210:31fd:1::13
owncloud A 10.100.0.15
ipv4.owncloud A 10.100.0.15
owncloud AAAA 2a01:4f8:210:31fd:1::f
ipv6.owncloud AAAA 2a01:4f8:210:31fd:1::f
test A 10.100.0.20
ipv4.test A 10.100.0.20
test AAAA 2a01:4f8:210:31fd:1::14
ipv6.test AAAA 2a01:4f8:210:31fd:1::14