From e3354dfe8ed1ab9002567a1b5e860ca1de955e8c Mon Sep 17 00:00:00 2001 From: root Date: Mon, 18 Aug 2014 10:29:43 +0200 Subject: [PATCH] first commit --- container.json | 170 ++++++++++++++++++ default.conf | 22 +++ hooks/ansible | 11 ++ hooks/cleanup-lxc-config | 27 +++ hooks/create-lxc-config | 124 +++++++++++++ hooks/remove-journal | 10 ++ hooks/setup-machine-id | 11 ++ hooks/update-container | 4 + hooks/update-zone | 85 +++++++++ lxc.conf | 2 + templates/config.erb | 6 + templates/lxc-zone.erb | 21 +++ templates/rdns-zone.erb | 11 ++ ...0.d.f.1.3.0.1.2.0.8.f.4.0.1.0.a.2.ip6.arpa | 48 +++++ zones/100.10.in-addr.arpa | 48 +++++ zones/lxc.zone | 108 +++++++++++ 16 files changed, 708 insertions(+) create mode 100644 container.json create mode 100644 default.conf create mode 100755 hooks/ansible create mode 100755 hooks/cleanup-lxc-config create mode 100755 hooks/create-lxc-config create mode 100755 hooks/remove-journal create mode 100755 hooks/setup-machine-id create mode 100755 hooks/update-container create mode 100755 hooks/update-zone create mode 100644 lxc.conf create mode 100644 templates/config.erb create mode 100644 templates/lxc-zone.erb create mode 100644 templates/rdns-zone.erb create mode 100644 zones/1.0.0.0.d.f.1.3.0.1.2.0.8.f.4.0.1.0.a.2.ip6.arpa create mode 100644 zones/100.10.in-addr.arpa create mode 100644 zones/lxc.zone diff --git a/container.json b/container.json new file mode 100644 index 0000000..fc1c298 --- /dev/null +++ b/container.json @@ -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 + } + } +} \ No newline at end of file diff --git a/default.conf b/default.conf new file mode 100644 index 0000000..d5495ed --- /dev/null +++ b/default.conf @@ -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 diff --git a/hooks/ansible b/hooks/ansible new file mode 100755 index 0000000..4a2015d --- /dev/null +++ b/hooks/ansible @@ -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" diff --git a/hooks/cleanup-lxc-config b/hooks/cleanup-lxc-config new file mode 100755 index 0000000..f0831d3 --- /dev/null +++ b/hooks/cleanup-lxc-config @@ -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 diff --git a/hooks/create-lxc-config b/hooks/create-lxc-config new file mode 100755 index 0000000..54abf21 --- /dev/null +++ b/hooks/create-lxc-config @@ -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 diff --git a/hooks/remove-journal b/hooks/remove-journal new file mode 100755 index 0000000..abb8614 --- /dev/null +++ b/hooks/remove-journal @@ -0,0 +1,10 @@ +#!/bin/bash + +set -xeu + +JOURNAL="$LXC_ROOTFS_PATH/var/log/journal" + +if [ -d "$JOURNAL" ]; then + cd $JOURNAL + rm -rf * +fi diff --git a/hooks/setup-machine-id b/hooks/setup-machine-id new file mode 100755 index 0000000..4e5d900 --- /dev/null +++ b/hooks/setup-machine-id @@ -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" diff --git a/hooks/update-container b/hooks/update-container new file mode 100755 index 0000000..748b3d8 --- /dev/null +++ b/hooks/update-container @@ -0,0 +1,4 @@ +#!/bin/bash + +pacman -r "$LXC_ROOTFS_PATH" -Syu --noconfirm +exit $? diff --git a/hooks/update-zone b/hooks/update-zone new file mode 100755 index 0000000..0cbe481 --- /dev/null +++ b/hooks/update-zone @@ -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") diff --git a/lxc.conf b/lxc.conf new file mode 100644 index 0000000..161f096 --- /dev/null +++ b/lxc.conf @@ -0,0 +1,2 @@ +lxc.lxcpath = /data/containers +lxc.bdev.zfs.root = data/containers diff --git a/templates/config.erb b/templates/config.erb new file mode 100644 index 0000000..cd23a10 --- /dev/null +++ b/templates/config.erb @@ -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)] %> diff --git a/templates/lxc-zone.erb b/templates/lxc-zone.erb new file mode 100644 index 0000000..8ecebd4 --- /dev/null +++ b/templates/lxc-zone.erb @@ -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 -%> diff --git a/templates/rdns-zone.erb b/templates/rdns-zone.erb new file mode 100644 index 0000000..f7e9393 --- /dev/null +++ b/templates/rdns-zone.erb @@ -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 -%> diff --git a/zones/1.0.0.0.d.f.1.3.0.1.2.0.8.f.4.0.1.0.a.2.ip6.arpa b/zones/1.0.0.0.d.f.1.3.0.1.2.0.8.f.4.0.1.0.a.2.ip6.arpa new file mode 100644 index 0000000..7200f22 --- /dev/null +++ b/zones/1.0.0.0.d.f.1.3.0.1.2.0.8.f.4.0.1.0.a.2.ip6.arpa @@ -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. diff --git a/zones/100.10.in-addr.arpa b/zones/100.10.in-addr.arpa new file mode 100644 index 0000000..f72ef22 --- /dev/null +++ b/zones/100.10.in-addr.arpa @@ -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. diff --git a/zones/lxc.zone b/zones/lxc.zone new file mode 100644 index 0000000..15d23d6 --- /dev/null +++ b/zones/lxc.zone @@ -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