#!/usr/bin/env ruby require 'optparse' require 'fileutils' require 'net/http' require 'etc' require 'facter' options = {} OptionParser.new do |opts| opts.banner = "Usage: generate_container.rb [options]" opts.on("--fqdn [FQDN]", "FQDN") do |f| options[:fqdn] = f end opts.on("--ip [IP]", "IP address") do |i| options[:ip] = i end opts.on("--cidr [CIDR]", "CIDR") do |c| options[:cidr] = c end opts.on("--gateway [GATEWAY]", "Gateway") do |g| options[:gateway] = g end opts.on("--ip6 [IPv6]", "IPv6") do |i6| options[:ip6] = i6 end opts.on("--cidr6 [CIDR]", "CIDR v6") do |c6| options[:cidr6] = c6 end opts.on("--gateway6 [GATEWAY]", "Gateway v6") do |g6| options[:gateway6] = g6 end opts.on("--os [VERSION]", "OS version") do |os| options[:os] = os end opts.on("--version [VERSION]", "OS version") do |v| options[:version] = v end opts.on("--puppet [PUPPET SERVER]", "Puppet server") do |p| options[:puppet] = p end opts.on("--ippuppet [IP PUPPET SERVER]", "IP Puppet server") do |ph| options[:ippuppet] = ph end opts.on("--ip6puppet [IPv6 PUPPET SERVER]", "IPv6 Puppet server") do |p6h| options[:ip6puppet] = p6h end opts.on("--dns [DNS SERVER]", "DNS Server") do |dn| options[:dns] = dn end opts.on("--idhypervisor [ID Hypervisor]", "ID Hypervisor") do |ih| options[:idhypervisor] = ih end opts.on("--provider [Provider]", "Provider") do |pr| options[:provider] = pr end end.parse! ip_split = options[:ip].split(".") mac = "52:54:00:#{options[:idhypervisor]}0:"+ip_split[2].to_i.to_s(16).rjust(2,'0')+":"+ip_split[3].to_i.to_s(16).rjust(2,'0') mac6 = "52:54:00:#{options[:idhypervisor]}1:"+ip_split[2].to_i.to_s(16).rjust(2,'0')+":"+ip_split[3].to_i.to_s(16).rjust(2,'0') Dir.chdir('/srv') if options[:version].nil? then options[:version] = "7" end if options[:os].nil? then options[:os] = "centos" end if options[:os] == "fedora" then options[:repo] = "--disablerepo='*' --enablerepo='fedora'" else options[:repo] = "" end if not File.exist?("/srv/#{options[:os]}#{options[:version]}.tar") then system("yum -y --installroot=/srv/tmprootfs --releasever=#{options[:version]} #{options[:repo]} --nogpg install systemd passwd yum #{options[:os]}-release vim-minimal openssh-server procps-ng iputils emacs-nox wget curl git") system("yum -y --installroot=/srv/tmprootfs --releasever=#{options[:version]} #{options[:repo]} --nogpg groupinstall 'Minimal Install'") system("yum -y --installroot=/srv/tmprootfs --releasever=#{options[:version]} #{options[:repo]} --nogpg groupinstall 'Development Tools'") Dir.chdir("tmprootfs") system("tar cvf #{options[:os]}#{options[:version]}.tar --exclude '#{options[:os]}#{options[:version]}.tar' .") Dir.chdir("..") File.rename("tmprootfs/#{options[:os]}#{options[:version]}.tar","#{options[:os]}#{options[:version]}.tar") FileUtils.rm_rf("tmprootfs") end options[:hostname] = options[:fqdn].split(".").first Dir.chdir('/var/lib/lxc') if not File.exists?(options[:hostname]) FileUtils.mkdir_p("/var/lib/lxc/"+options[:hostname]+"/rootfs") Dir.chdir("/var/lib/lxc/"+options[:hostname]+"/rootfs") system("tar xvf /srv/#{options[:os]}#{options[:version]}.tar") end pubkey = File.read("/root/.ssh/id_ecdsa.pub") chroot = Process.fork do Dir.chdir("/") Dir.chroot("/var/lib/lxc/"+options[:hostname]+"/rootfs") filename = "/etc/sysconfig/network" outdata = "NETWORKING=yes\n" File.open(filename, 'w+') do |out| out << outdata end outdata = "#{options[:fqdn]}\n" File.open("/etc/hostname", 'w+') do |out| out << outdata end outdata = <<EOF net.ipv6.conf.all.dad_transmits = 0 net.ipv6.conf.all.accept_dad = 0 net.ipv6.conf.lo.dad_transmits = 0 net.ipv6.conf.lo.accept_dad = 0 net.ipv6.conf.eth1.dad_transmits = 0 net.ipv6.conf.eth1.accept_dad = 0 EOF File.open("/etc/sysctl.conf", 'w+') do |out| out << outdata end outdata = <<EOF DEVICE=eth0 TYPE=Ethernet IPADDR=#{options[:ip]} PREFIX=#{options[:cidr]} GATEWAY=#{options[:gateway]} ONBOOT=yes BOOTPROTO=none NM_CONTROLLED=no DELAY=0 EOF File.open("/etc/sysconfig/network-scripts/ifcfg-eth0", 'w+') do |out| out << outdata end if(options[:provider] == "ovh") then outdata = <<EOF DEVICE=eth1 TYPE=Ethernet IPV6INIT=yes IPV6_AUTOCONF=no IPV6ADDR=#{options[:ip6]}/#{options[:cidr6]} ONBOOT=yes BOOTPROTO=none NM_CONTROLLED=no DELAY=0 EOF elsif(options[:provider] == "hetzner") then outdata = <<EOF DEVICE=eth1 TYPE=Ethernet IPV6INIT=yes IPV6_AUTOCONF=no IPV6ADDR=#{options[:ip6]}/#{options[:cidr6]} IPV6_DEFAULTGW=#{options[:gateway6]} IP6_DEFAULTDEV=eth1 ONBOOT=yes BOOTPROTO=none NM_CONTROLLED=no DELAY=0 EOF end File.open("/etc/sysconfig/network-scripts/ifcfg-eth1", 'w+') do |out| out << outdata end if(options[:provider] == "ovh") then outdata = <<EOF if [[ "$1" == "eth1" ]] then /sbin/ip -f inet6 route add #{options[:gateway6]} dev eth1 /sbin/ip -f inet6 route add default via #{options[:gateway6]} fi EOF File.open("/sbin/ifup-local", 'w+') do |out| out << outdata end File.chmod(0755,"/sbin/ifup-local") end outdata = <<EOF nameserver #{options[:dns]} EOF File.open("/etc/resolv.conf", 'w+') do |out| out << outdata end outdata = <<EOF 127.0.0.1 localhost #{options[:ip]} #{options[:fqdn]} #{options[:hostname]} #{options[:ip6puppet]} #{options[:puppet]} ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters EOF File.open("/etc/hosts", 'w+') do |out| out << outdata end Dir.mkdir("/root/.ssh") if not File.directory?("/root/.ssh") filename = "/root/.ssh/authorized_keys" File.open(filename, "w+") do |out| out << pubkey end FileUtils.rm('/etc/systemd/system/multi-user.target.wants/auditd.service') FileUtils.rm('/etc/systemd/system/sockets.target.wants/avahi-daemon.socket') FileUtils.rm('/etc/systemd/system/multi-user.target.wants/avahi-daemon.service') FileUtils.rm('/etc/systemd/system/dbus-org.freedesktop.Avahi.service') FileUtils.rm('/etc/systemd/system/multi-user.target.wants/kdump.service') FileUtils.rm('/usr/lib/systemd/system/getty.target') FileUtils.rm("/etc/systemd/system/getty.target.wants/getty@tty1.service") FileUtils.symlink("/dev/null","/etc/systemd/system/getty.target.wants/getty@tty1.service") FileUtils.symlink("/usr/lib/systemd/system/poweroff.target","/etc/systemd/system/sigpwr.target") FileUtils.symlink("/dev/null","/etc/systemd/system/systemd-udevd.service") FileUtils.symlink("/dev/null","/etc/systemd/system/systemd-udevd-control.socket") FileUtils.symlink("/dev/null","/etc/systemd/system/systemd-udevd-kernel.socket") FileUtils.symlink("/dev/null","/etc/systemd/system/proc-sys-fs-binfmt_misc.automount") system("rpm -ivh http://yum.puppetlabs.com/puppetlabs-release-el-7.noarch.rpm") system("yum -y install puppet") filename = "/etc/puppet/puppet.conf" outdata = File.read(filename) outdata.gsub!(/\[agent\]/, "[agent]\nserver=#{options[:puppet]}\ncertname=#{options[:fqdn]}") if not outdata.include?("certname") File.open(filename, 'w+') do |out| out << outdata end outdata = <<EOF START=no DAEMON_OPTS="" export LANG=en_US.UTF-8 EOF File.open("/etc/default/puppet", 'w+') do |out| out << outdata end end Process.wait outdata = <<EOF # For additional config options, please look at lxc.container.conf(5) lxc.network.type = veth lxc.network.flags = up lxc.network.link = br-int lxc.network.hwaddr = #{mac} lxc.network.type = veth lxc.network.flags = up lxc.network.link = br-ex lxc.network.hwaddr = #{mac6} lxc.rootfs = /var/lib/lxc/#{options[:hostname]}/rootfs lxc.arch = x86_64 lxc.utsname = #{options[:fqdn]} lxc.autodev = 1 lxc.devttydir = lxc lxc.tty = 4 lxc.pts = 1024 lxc.kmsg = 0 # Mount entries lxc.mount.auto = proc:mixed sys:ro # Ensure hostname is changed on clone lxc.hook.clone = /usr/share/lxc/hooks/clonehostname # hostname(1). # lxc.cap.drop = sys_admin # lxc.cap.drop = net_raw # breaks dhcp/ping # lxc.cap.drop = setgid # breaks login (initgroups/setgroups) # lxc.cap.drop = dac_read_search # breaks login (pam unix_chkpwd) # lxc.cap.drop = setuid # breaks sshd,nfs statd # lxc.cap.drop = audit_control # breaks sshd (set_loginuid failed) # lxc.cap.drop = audit_write # lxc.cap.drop = setpcap # big big login delays in Fedora 20 systemd # lxc.cap.drop = mac_admin mac_override # lxc.cap.drop = setfcap lxc.cap.drop = sys_module sys_nice sys_pacct lxc.cap.drop = sys_rawio sys_time # Control Group devices: all denied except those whitelisted lxc.cgroup.devices.deny = a # Allow any mknod (but not reading/writing the node) lxc.cgroup.devices.allow = c *:* m lxc.cgroup.devices.allow = b *:* m lxc.cgroup.devices.allow = c 1:3 rwm # /dev/null lxc.cgroup.devices.allow = c 1:5 rwm # /dev/zero lxc.cgroup.devices.allow = c 1:7 rwm # /dev/full lxc.cgroup.devices.allow = c 5:0 rwm # /dev/tty lxc.cgroup.devices.allow = c 1:8 rwm # /dev/random lxc.cgroup.devices.allow = c 1:9 rwm # /dev/urandom lxc.cgroup.devices.allow = c 136:* rwm # /dev/tty[1-4] ptys and lxc console lxc.cgroup.devices.allow = c 5:2 rwm # /dev/ptmx pty master # Blacklist some syscalls which are not safe in privileged # containers lxc.seccomp = /usr/share/lxc/config/common.seccomp EOF filename = "/var/lib/lxc/#{options[:hostname]}/config" File.open(filename, 'w') do |out| out << outdata end