diff --git a/.gitignore b/.gitignore index d986f6ad3..2b2c8d5ce 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ templates/lxc-cirros templates/lxc-debian templates/lxc-download templates/lxc-fedora +templates/lxc-gentoo templates/lxc-openmandriva templates/lxc-opensuse templates/lxc-oracle diff --git a/configure.ac b/configure.ac index 851426737..190dcdfe9 100644 --- a/configure.ac +++ b/configure.ac @@ -632,22 +632,23 @@ AC_CONFIG_FILES([ hooks/Makefile templates/Makefile + templates/lxc-alpine + templates/lxc-altlinux + templates/lxc-archlinux + templates/lxc-busybox + templates/lxc-centos templates/lxc-cirros templates/lxc-debian templates/lxc-download + templates/lxc-fedora + templates/lxc-gentoo + templates/lxc-openmandriva + templates/lxc-opensuse + templates/lxc-oracle + templates/lxc-plamo + templates/lxc-sshd templates/lxc-ubuntu templates/lxc-ubuntu-cloud - templates/lxc-opensuse - templates/lxc-busybox - templates/lxc-centos - templates/lxc-fedora - templates/lxc-openmandriva - templates/lxc-oracle - templates/lxc-altlinux - templates/lxc-sshd - templates/lxc-archlinux - templates/lxc-alpine - templates/lxc-plamo src/Makefile src/lxc/Makefile diff --git a/templates/Makefile.am b/templates/Makefile.am index ff0a6039c..ac870a1c0 100644 --- a/templates/Makefile.am +++ b/templates/Makefile.am @@ -10,6 +10,7 @@ templates_SCRIPTS = \ lxc-debian \ lxc-download \ lxc-fedora \ + lxc-gentoo \ lxc-openmandriva \ lxc-opensuse \ lxc-oracle \ diff --git a/templates/lxc-gentoo.in b/templates/lxc-gentoo.in new file mode 100644 index 000000000..1404390a9 --- /dev/null +++ b/templates/lxc-gentoo.in @@ -0,0 +1,828 @@ +#!/bin/bash + +# +# LXC template for gentoo +# +# Author: Guillaume Zitta +# +# Widely inspired from lxc-gentoo script at https://github.com/globalcitizen/lxc-gentoo +# +# this version is reworked with : +# - out of the lxc-create compat +# - vanilla gentoo config +# - ready to use cache +# + +# Ensure strict root's umask doesen't render the VM unusable +umask 022 + +################################################################################ +# Various helper functions +################################################################################ + +# param: $1: the name of the lock +# param: $2: the timeout for the lock +# The rest contain the command to execute and its parameters +execute_exclusively() +{ + mkdir -p @LOCALSTATEDIR@/lock/subsys/ + + local lock_name="$1" + local timeout="$2" + shift 2 + + { + printf "Attempting to obtain an exclusive lock (timeout: %s sec) named \"%s\"...\n" "${timeout}" "$lock_name" + + flock -x -w "${timeout}" 50 + + if [[ $? -ne 0 ]]; then + printf " => unable to obtain lock, aborting.\n" + return 2 + else + printf " => done.\n" + fi + + printf " => Executing \"%s\"\n" "$*" + "$@" + retval=$? + } 50> "@LOCALSTATEDIR@/lock/subsys/lxc-gentoo-${lock_name}" + return $retval +} + +# a die function is always a good idea +die() +{ + printf "\n[the last exit code leading to this death was: %s ]\n" "$?" + local retval="$1" + shift 1 + printf "$@" + exit "$retval" +} + +# gentoo arch/variant detection +set_default_arch() +{ + printf "### set_default_arch: default arch/variant autodetect...\n" + arch=$(arch) + if [[ $arch =~ i.86 ]]; then + arch="x86" + variant="x86" + elif [[ $arch == "x86_64" ]]; then + arch="amd64" + variant="amd64" + elif [[ $arch =~ arm.* ]]; then + arch="arm" + variant="armv7a" + else + #who knows, it may work... + printf " => warn: unexpected arch:${arch} let me knows if it works :)\n" + variant="${arch}" + fi + printf " => Got: arch=%s variant=%s\n" "${arch}" "${variant}" +} + +store_user_message() +{ + user_message="${user_message}=> $@\n" +} + +################################################################################ +# CACHE Preparation +################################################################################ +# during setup cachedir is $cacheroot/partial-$arch-$variant +# at the end, it will be $cacheroot/rootfs-$arch-$variant + +cache_setup(){ + partialfs="${cacheroot}/partial-${arch}-${variant}" + + #if cache exists and flush not needed, return + [[ -d "${cachefs}" && -z "${flush_cache}" ]] && return 0 + + printf "###### cache_setup(): doing cache preparation\n" + local retval=1 + + #clean from failed previous run + rm -rf "${partialfs}" + mkdir -p "${partialfs}" + + #let's go + cache_precheck && \ + cache_stage3 && \ + cache_portage && \ + cache_inittab && \ + cache_net && \ + cache_dev && \ + cache_openrc && \ + rm -rf "${cachefs}" && \ + mv "${partialfs}" "${cachefs}" && \ + printf "###### cache_setup: Cache should be ready\n" + + return $? +} + +cache_precheck() +{ + printf "### cache_precheck(): doing some pre-start checks ...\n" + # never hurts to have a fail-safe. + [[ -n "${cacheroot//\/}" ]] \ + || die 8 "\$cacheroot (%s) IS EMPTY OR MADE OF ONLY DIRECTORY SEPERATORS, THIS IS *VERY* BAD!\n" "${cacheroot}" +} + +#get latest stage3 tarball +cache_stage3() +{ + printf "### cache_stage3(): stage3 cache deployment...\n" + + if [ -z "${tarball}" ]; then + #variables init + local stage3_baseurl="${mirror}/releases/${arch}/autobuilds" + + # get latest-stage3....txt file for subpath + local stage3_pointer="${stage3_baseurl}/latest-stage3-${variant}.txt" + + printf "Determining path to latest Gentoo %s (%s) stage3 archive...\n" "${arch}" "${variant}" + printf " => downloading and processing %s\n" "${stage3_pointer}" + + local stage3_latest_tarball=$(wget -q -O - "${stage3_pointer}" | tail -n1 ) \ + || die 6 "Error: unable to fetch\n" + + printf " => Got: %s\n" "${stage3_latest_tarball}" + + printf "Downloading/untarring the actual stage3 tarball...\n" + wget -O - "${stage3_baseurl}/${stage3_latest_tarball}" | tar -xjpf - -C "${partialfs}" \ + || die 6 "Error: unable to fetch or untar\n" + printf " => extracted to: %s\n" "${partialfs}" + else + printf "Extracting the stage3 tarball...\n" + tar -xpf "${tarball}" -C "${partialfs}" || die 6 "unable to untar ${tarball} to ${partialfs}" + fi + + #check if it chroots + printf "chroot test..." + chroot ${partialfs} /bin/true || die 1 "Error: chroot %s /bin/true, failed" "${partialfs}" + printf " OK\n" + printf " => stage3 cache extracted in : %s\n" "${partialfs}" + return 0 +} + +cache_portage() +{ + printf "### cache_portage: caching portage tree tarball...\n" + [[ -z "${flush_cache}" && -f "${portage_cache}" ]] && return 0 + + rm -f ${portage_cache} + + printf "Downloading Gentoo portage (software build database) snapshot...\n" + execute_exclusively portage 60 wget -O "${portage_cache}" "${mirror}/snapshots/portage-latest.tar.bz2" \ + || die 6 "Error: unable to fetch\n" + printf " => done.\n" +} + +# custom inittab +cache_inittab() +{ + printf "### cache_inittab: tuning inittab...\n" + + INITTAB="${partialfs}/etc/inittab" + + [[ -w "$INITTAB" ]] || die 1 "Error: $INITTAB is not writeable" + + # create console + echo "# Lxc main console" >> "$INITTAB" + echo "1:12345:respawn:/sbin/agetty -a root --noclear 115200 console linux" >> "$INITTAB" + + # finally we add a pf line to enable clean shutdown on SIGPWR (issue 60) + echo "# clean container shutdown on SIGPWR" >> "$INITTAB" + echo "pf:12345:powerwait:/sbin/halt" >> "$INITTAB" + + # we also blank out /etc/issue here in order to prevent delays spawning login + # caused by attempts to determine domainname on disconnected containers + sed -i 's/[\][Oo]//g' "${partialfs}/etc/issue" +} + +cache_net() +{ + printf "### cache_net: doing some useful net tuning...\n" + # useful for chroot + # /etc/resolv.conf + grep -i 'search ' /etc/resolv.conf > "${partialfs}/etc/resolv.conf" + grep -i 'nameserver ' /etc/resolv.conf >> "${partialfs}/etc/resolv.conf" + + # fix boot-time interface config wipe under aggressive cap drop + # (openrc 0.9.8.4 ~sep 2012 - https://bugs.gentoo.org/show_bug.cgi?id=436266) + # initial warkaround was: sed -i -e 's/^#rc_nostop=""/rc_nostop="net.eth0 net.lo"/' "${partialfs}/etc/rc.conf" + # but this one does not depends on interfaces names + echo 'rc_keyword="-stop"' >> "${partialfs}/etc/conf.d/net" +} + +cache_dev() +{ + printf "### cache_dev(): /dev tuning...\n" + + #Wait for https://bugs.gentoo.org/show_bug.cgi?id=496054 + mkdir "${partialfs}/dev/pts" + + if [ -n "${nettun}" ]; then + mkdir -m 755 "${partialfs}/net" + mknod -m 666 "${partialfs}/net/tun" c 10 200 + fi + + return 0 +} + +# fix openrc system +cache_openrc() +{ + printf "### cache_openrc(): doing openrc tuning\n" + + #Wait for https://bugs.gentoo.org/show_bug.cgi?id=496054 + chroot "${partialfs}" sed s/-lxc//g -i "/etc/init.d/devfs" + + return 0 +} + +################################################################################ +# CONTAINER Preparation +################################################################################ + +container_setup() { + printf "##### container_setup(): starting container setup\n" + + #in most cases lxc-create should have provided a copy of default lxc.conf + #let's tag where template starts, or just create the files + echo '### lxc-gentoo template stuff starts here' >> "$path/config" + + #Determine rootfs + #If backingstore was specified, lxc.rootfs should be present or --rootfs did the rootfs var creation + if [ -z "${rootfs}" ]; then + rootfs=`awk -F= '$1 ~ /^lxc.rootfs/ { print $2 }' "$path/config" 2>/dev/null` + if [ -z "${rootfs}" ]; then + #OK it's default + rootfs="${path}/rootfs" + fi + fi + store_user_message "rootfs of container is : ${rootfs}" + store_user_message "config of container is : ${path}/config" + + container_precheck && \ + container_rootfs && \ + container_consoles && \ + container_tz && \ + container_portage && \ + container_net && \ + container_hostname && \ + container_auth && \ + container_conf + if [ $? -ne 0 ]; then + die 1 "container_setup(): one step didn't complete, sorry\n" + fi + + printf "###### container_setup(): container should be ready to start!\n" + printf "\n\n" + printf "You could now use you container with: lxc-start -n %s\n" "${name}" + printf "little things you should know about your container:\n" + printf "${user_message}" + return 0 +} + +container_precheck() +{ + printf "### container_precheck(): doing some pre-start checks ...\n" + # never hurts to have a fail-safe. + [[ -n "${name//\/}" ]] \ + || die 8 "\$name (%s) IS EMPTY OR MADE OF ONLY DIRECTORY SEPERATORS, THIS IS *VERY* BAD!\n" "${name}" + + [[ -n "${rootfs//\/}" ]] \ + || die 8 "\$rootfs (%s) IS EMPTY OR MADE OF ONLY DIRECTORY SEPERATORS, THIS IS *VERY* BAD!\n" "${rootfs}" + + [[ -n "${cachefs//\/}" ]] \ + || die 8 "\$cachefs (%s) IS EMPTY OR MADE OF ONLY DIRECTORY SEPERATORS, THIS IS *VERY* BAD!\n" "${cachefs}" + + # check if the rootfs already exists + [[ -d "${rootfs}/etc" ]] && die 18 "Error: \$rootfs (%s) already exists!" "${rootfs}" + + # check cache + [[ ! -d "${cachefs}/etc" ]] && die 1 "Error: \$cachefs (%s) not found!" "${cachefs}" + + return 0 +} + +container_rootfs() +{ + printf "#### container_rootfs(): copying rootfs %s from cache %s ...\n" "${rootfs}" "${cachefs}" + tar -c -f - -C "${cachefs}" . | tar -x -p -f - -C "${rootfs}" || die 1 "Error: cache copy to rootfs failed" + + printf "chroot test..." + chroot "${rootfs}" /bin/true || die 1 "Error: 'chroot %s /bin/true' failed" + printf " OK\n" + + printf " => done\n" + return 0 +} + +container_consoles() { + printf "#### container_consoles(): setting container consoles ...\n" + + # disable unwanted ttys + if [[ ${tty} < 6 ]]; then + local mindis=$(( ${tty} + 1 )) + sed -i "s/^c[${mindis}-6]/#&/" "${rootfs}/etc/inittab" + fi + printf " => main console + ${tty} ttys\n" + + if [[ -z "${autologin}" ]]; then + sed 's/agetty -a root/agetty/' -i "${rootfs}/etc/inittab" + elif [[ "${user}" != "root" ]]; then + sed "s/agetty -a root/agetty -a ${user}/" -i "${rootfs}/etc/inittab" + printf " => Autologin on main console for %s enabled\n" "${user}" + [[ -z "${forced_password}" ]] && unset password + store_user_message "${user} has autologin on main console" + else + printf " => Autologin on main console for root enabled\n" + [[ -z "${forced_password}" ]] && unset password + store_user_message "${user} has autologin on main console" + fi + printf " => done\n" +} + +container_tz() +{ + printf "#### container_tz(): setting container timezone ...\n" + + #let's try to copy it from host + if [ -L "/etc/localtime" ]; then + #host has a symlink + #let see if we can reproduct symlink + target=$(readlink /etc/localtime) + if [[ "$target" != "" ]]; then + if [ -f "${rootfs}/${target}" ]; then + #same target exists in container + chroot "${rootfs}" ln -sf "${target}" "/etc/localtime" + printf " => host symlink reproducted in container : %s\n" "${target}" + store_user_message "timezone copyed from host" + return 0 + fi + fi + fi + + if [ -e /etc/localtime ]; then + # duplicate host timezone + cat /etc/localtime > "${rootfs}/etc/localtime" + printf " => host localtime copyed to container\n" + store_user_message "timezone was staticly copyed from host" + else + # otherwise set up UTC + chroot "${rootfs}" ln -sf /usr/share/zoneinfo/UTC /etc/localtime + printf " => fallback: fixed to UTC\n" + store_user_message "timezone was fixed to UTC" + fi +} + + +container_portage() +{ + printf "#### container_portage(): setting container portage... \n" + + #default entry for conf + portage_mount="#container set with private portage tree, no mount here" + + printf "Warnings are normal here, don't worry\n" + #container repos detection + if chroot ${rootfs} portageq get_repo_path / gentoo > /dev/null ; then + portage_container="$(chroot ${rootfs} portageq get_repo_path / gentoo)" + else + die 1 "Failed to figure out container portage tree location with portageq get_repo_path / gentoo\n" + fi + + if [[ -n "${private_portage}" ]]; then + container_private_portage + return 0 + fi + + if [ -z "${portage_dir}" ]; then + #gentoo host detection + printf "trying to guess portage_dir from host...\n" + portage_dir="$(portageq get_repo_path / gentoo 2>/dev/null)" + if [ ! -d "${portage_dir}/profiles" ]; then + printf " => host portage detection failed (not gentoo host), fallback to private portage tree\n" + container_private_portage + return 0 + fi + else + if [ ! -d "${portage_dir}/profiles" ]; then + die 1 "specified portage_dir (%s) does not contains profiles, is it a portage tree ?\n" "${portage_dir}" + fi + fi + + # if we are here, we have shared portage_dir + #ensure dir exists + chroot "${rootfs}" mkdir ${portage_container} + portage_mount="#container set with shared portage + lxc.mount.entry=${portage_dir} ${portage_container/\//} none ro,bind 0 0" + store_user_message "container has a shared portage from host's ${portage_dir} to ${portage_container/\//}" + #Let's propose binary packages + cat <<- EOF >> "${rootfs}/etc/portage/make.conf" + # enable this to store built binary packages + #FEATURES="\$FEATURES buildpkg" + + # enable this to use built binary packages + #EMERGE_DEFAULT_OPTS="\${EMERGE_DEFAULT_OPTS} --usepkg" + + # enable and *tune* this kind of entry to slot binaries, specialy if you use multiples archs and variants + #PKGDIR="\${PKGDIR}/amd64 + #or PKGDIR="\${PKGDIR}/hardened" +EOF + printf " => portage stuff done, see /etc/portage/make.conf for additionnal tricks\n" + +} + +container_private_portage() +{ + #called from container_portage() do not call directly from container_setup + printf "# untaring private portage to %s from %s ... \n" "${rootfs}/${portage_container}" "${portage_cache}" + mkdir -p "${rootfs}/${portage_container}" + execute_exclusively portage 60 tar -xp --strip-components 1 -C "${rootfs}/${portage_container}" -f "${portage_cache}" \ + || die 2 "Error: unable to extract the portage tree.\n" + store_user_message "container has its own portage tree at ${portage_container}" + printf "=> done\n" +} + +#helper func for container_genconf_net() +nic_write() +{ + #display with gentoo's confd.net format + echo "config_${nic_name}=\"${nic_conf}\"" + #add to managed list + [[ "${nic_conf}" == "dhcp" ]] && nic_managed="${nic_managed} ${nic_name}" + [[ "${nic_conf}" == "null" ]] && nic_unmanaged="${nic_unmanaged} ${nic_name}" + [[ -z "${nic_hwaddr}" && ${nic_type} == "veth" ]] && nic_wo_hwaddr="${nic_wo_hwaddr} ${nic_name}" + nic_writed=1 +} + +#Analyse lxc.conf and print conf.d/net content +container_conf_net() +{ + local file=${1} + [[ -z "${nic_last}" ]] && nic_last=-1 + [[ -z "${nic_named}" ]] && nic_named=0 + OLDIFS=$IFS + IFS=" +" + #I'll drink champagne the day we do templates in python + #let's do some drity bash things + for line in $( sed -r "s/[ ]*=[ ]*/_real_ugly_sep_42_/" "${file}" ); do + key=$(echo "${line}" | sed 's/_real_ugly_sep_42_.*$//') + value=$(echo "${line}" | sed 's/^.*_real_ugly_sep_42_//') + + #new nic ! + if [[ "${key}" == "lxc.network.type" ]]; then + #we don't know what to do with it. + [[ "${value}" == "empty" ]] && continue + + #write conf from previous loops + [[ "${nic_writed}" == "0" ]] && nic_write + + #init defaults + let nic_last=nic_last+1 + + nic_writed=0 + #if 1 named between 2 not named: last is eth1 + #=> Number is ID munis number of named NIC before + nic_name="eth$(( ${nic_last} - ${nic_named} ))" + nic_conf="dhcp" + nic_type="${value}" + fi + + if [[ "${key}" == "lxc.network.hwaddr" ]]; then + nic_hwaddr=1 + fi + + if [[ "${key}" =~ ^lxc.network.ipv(4|6) ]]; then + #tell openrc to not manage this NIC as LXC set there address + nic_conf="null" + fi + if [[ "${key}" =~ ^lxc.network.name ]]; then + nic_name="${value}" + let nic_named=nic_named+1 + fi + if [[ "${key}" == "lxc.include" ]]; then + #recursive into include + container_conf_net "${value}" + fi + done + #write conf from previous loops + [[ "${nic_writed}" == "0" ]] && nic_write + IFS=$OLDIFS +} + +container_net() +{ + printf "container_net(): setting container network conf... \n" + + #Analyse network configuration in config + container_conf_net "$path/config" >> "${rootfs}/etc/conf.d/net" + + # found how much nic finaly have + nic_count=$(( ${nic_last} + 1 )) + + # unless openrc manage a nic, we now have to force openrc to automatic + # provision of the 'net' dep. If we do not, network dependent services + # will fail to load + if [[ -z "${nic_managed}" ]]; then + #tell openrc that lxc already did the work + echo 'rc_provide="net"' >> "$CACHE/etc/rc.conf" + fi + + #No NIC ? + if [[ ${nic_count} == 0 ]]; then + #If no Nic, no need to continue + bridge=$(brctl show | awk 'NR==2 {print $1}') + if [[ "${bridge}" != "" ]]; then + store_user_message "No network interface for this container +It's a pitty, you have bridge, ${bridge}. +If it is for Lxc, use it next time by adding this to your default.conf : +lxc.network.type = veth +lxc.network.link = ${bridge} +lxc.network.flags = up +lxc.network.hwaddr = fe:xx:xx:xx:xx:xx" + return 0 + else + store_user_message "No network interface for this container" + return 0 + fi + fi + + #For each openrc managed nic, activate + for nic in ${nic_managed} + do + chroot "${rootfs}" ln -s net.lo "/etc/init.d/net.${nic}" + chroot "${rootfs}" rc-update add net.${nic} default + done + + #Warn about dynamic hwaddr + if [[ -n "${nic_wo_hwaddr}" ]]; then + store_user_message "Warning, these veth NIC don't have fixed hwaddr : +${nic_wo_hwaddr} + +see http://lists.linuxcontainers.org/pipermail/lxc-devel/2013-December/006736.html +and man lxc.conf" + fi + + printf " => network conf done.\n" +} + +# custom hostname +container_hostname() +{ + printf "#### container_hostname(): setting hostname... \n" + printf "hostnale=%s\n" "${name}" > "${rootfs}/etc/conf.d/hostname" + printf " => done.\n" +} + +container_auth() +{ + printf "#### container_auth(): setting authentification... \n" + if [[ "${user}" != "root" ]]; then + printf " non root user requested, creating... \n" + chroot "${rootfs}" useradd --create-home -s /bin/bash "${user}" || die 1 "failed to create user ${user}" + printf " => user %s created\n" "${user}" + fi + store_user_message "Connection user is ${user}" + #Home of user + auth_home=$(chroot "${rootfs}" getent passwd "${user}" | cut -d : -f 6) + if [[ -r "${auth_key}" ]]; then + printf " deploying auth_key %s for user %s ...\n" "${auth_key}" "${user}" + mkdir -p "${rootfs}/${auth_home}/.ssh" + cat >> "${rootfs}/${auth_home}/.ssh/authorized_keys" + chroot "${rootfs}" chown "${user}:" "${auth_home}/.ssh/authorized_keys" + printf " => inserted public key in %s/.ssh/authorized_keys\n" "${auth_home}" + [[ -z "${forced_password}" ]] && unset password + store_user_message "${user} has the ssh key you gived us" + fi + + if [[ -n "${password}" ]]; then + printf " setting password for %s ...\n" "${user}" + echo "${user}:${password}" | chroot "${rootfs}" chpasswd || die 1 "failed to change password" + printf " => done. if you didn't specify , default is 'toor'\n" + if [[ -n "${forced_password}" ]]; then + store_user_message "${user} has the password you give for him" + else + store_user_message "${user} has the default password 'toor', please change it ASAP" + fi + fi + + printf " => done.\n" +} + +################################################################################ +# lxc configuration files +################################################################################ + +container_conf() +{ + printf "container_configuration(): making lxc configuration file... \n" + + #at this point if there + conf_file="${path}/config" + + if grep -q "^lxc.rootfs" "${conf_file}" ; then + #lxc-create already provided one + conf_rootfs_line="" + else + conf_rootfs_line="lxc.rootfs = $(readlink -f "${rootfs}")" + fi + if [[ "${arch}" == "x86" || "${arch}" == "amd64" ]]; then + local conf_arch_line="lxc.arch = ${arch}" + else + local conf_arch_line="# lxc.arch = ${arch}" + fi + + conf_lxc_cap_drop="sys_module mac_admin mac_override mknod sys_time" + conf_sysfs="lxc.mount.entry=sys sys sysfs defaults 0 0" + + #more aggressive configuration, for your safety. But less things may work + if [ -n "${more_secure}" ]; then + conf_lxc_cap_drop="${conf_lxc_cap_drop} audit_control audit_write dac_read_search fsetid ipc_owner linux_immutable setfcap sys_admin sys_boot sys_pacct sys_ptrace sys_rawio sys_resource sys_tty_config syslog" + conf_sysfs="# disabled for security, see http://blog.bofh.it/debian/id_413 +#lxc.mount.entry=sys sys sysfs defaults 0 0" + fi + + cat <<- EOF >> "${conf_file}" +# sets container architecture +# If desired architecture != amd64 or x86, then we leave it unset as +# LXC does not oficially support anything other than x86 or amd64. +${conf_arch_line} + +# console access +lxc.tty = ${tty} +lxc.pts = 1024 + +# set the hostname +lxc.utsname = ${name} + +${conf_rootfs_line} +${portage_mount} +${conf_sysfs} + +# this part is based on 'linux capabilities', see: man 7 capabilities +# eg: you may also wish to drop 'cap_net_raw' (though it breaks ping) +# +# WARNING: the security vulnerability reported for 'cap_net_admin' at +# http://mainisusuallyafunction.blogspot.com/2012/11/attacking-hardened-linux-systems-with.html +# via JIT spraying (the BPF JIT module disabled on most systems was used +# in the example, but others are suggested vulnerable) meant that users +# with root in a container, that capability and kernel module may escape +# the container. ALWAYS be extremely careful granting any process root +# within a container, use a minimal configuration at all levels - +# including the kernel - and multiple layers of security on any system +# where security is a priority. note that not only LXC but PAX (and +# others?) were vulnerable to this issue. +# +# conservative: lxc.cap.drop = sys_module mknod mac_override sys_boot +# aggressive follows. (leaves open: chown dac_override fowner ipc_lock kill lease net_admin net_bind_service net_broadcast net_raw setgid setuid sys_chroot) +# lxc.cap.drop = audit_control audit_write dac_read_search fsetid ipc_owner linux_immutable mac_admin mac_override mknod setfcap sys_admin sys_boot sys_module sys_pacct sys_ptrace sys_rawio sys_resource sys_time sys_tty_config syslog + +lxc.cap.drop = ${conf_lxc_cap_drop} + +${conf_mounts} + +# deny access to all devices by default, explicitly grant some permissions +# +# format is [c|b] [major|*]:[minor|*] [r][w][m] +# ^ ^ ^ +# char/block -' \`- device number \`-- read, write, mknod +# +# first deny all... +lxc.cgroup.devices.deny = a +# /dev/null and zero +lxc.cgroup.devices.allow = c 1:3 rw +lxc.cgroup.devices.allow = c 1:5 rw +# /dev/{,u}random +lxc.cgroup.devices.allow = c 1:9 rw +lxc.cgroup.devices.allow = c 1:8 r +# /dev/pts/* +lxc.cgroup.devices.allow = c 136:* rw +lxc.cgroup.devices.allow = c 5:2 rw +# /dev/tty{0,1} +lxc.cgroup.devices.allow = c 4:1 rwm +lxc.cgroup.devices.allow = c 4:0 rwm +# /dev/tty +lxc.cgroup.devices.allow = c 5:0 rwm +# /dev/console +lxc.cgroup.devices.allow = c 5:1 rwm +EOF + if [ -n "${nettun}" ]; then + cat <<- EOF >> "${conf_file}" +# /dev/net/tun +lxc.cgroup.devices.allow = c 10:200 rwm +EOF + fi + printf " => done.\n" +} + +usage() +{ + cat <] [-v|--variant ] [-P|--private-portage] [--portage-dir ] [-t|--tarball ] + [-F|--flush-cache] [-c|--cache-only] [-u|--user ] [-w|--password ] [-S|--auth-key ] + [-s|--more-secure] [-m|--mirror ] [--tty ] [--nettun] + +arch: the container architecture (e.g. amd64): defaults to host arch (currently: '${arch}') + If you choose one that needs emulation + tested: amd64, x86 + You could try any other gentoo arch, why not... + +variant: gentoo's Architecture variant as of dec 2013 : (currently: '${variant}') + for amd64 arch: amd64 (default), amd64-hardened+nomultilib, amd64-hardened, amd64-nomultilib, x32 + for x86 arch: i686 (default), i486, i686-hardened + for arm arch: armv7a (default), armv7a_hardfp, armv6j, armv6j_hardfp, armv5tel, armv4tl + +private-portage: by default, /usr/portage is mount-binded with host one if exists (currently: '${private_portage}') + this force container to have his own copy + +portage-dir: portage dir used for shared portage + by default the host on if any (currently: '${portage_dir}') + +tarball: force usage of local stage3 archive (currently: '${arch}') + If empty, latest will be downloaded + +flush-cache: do like there is no previous cache + +cache-only: just ensure cache is present + if cache exists and "flush-cache" not specified, does nothing + +user: user used in auth oriented options (currently: '${user}') + +password: password for user (currently: '${password}') + if default, usage of auth-key will disable password setting + +autologin: enable autologin for user (currently: '${autologin}') + This unset default password setting + +auth-key: SSH Public key file to inject into container for user (currently: '${auth_key}') + This unset default password setting + +more-secure: does some additional security agressive settings (may prevent things to run) (currently: '${more_secure}') + +mirror: gentoo mirror for download (currently: '${mirror}') + +tty: number of tty (6 max) (currently: '${tty}') + +nettun: enable creation of /dev/net/tun (for private container VPN) (currently: '${nettun}') +EOF + exit 0 +} + +#some overridable defaults +set_default_arch + +mirror="http://distfiles.gentoo.org" +user="root" +password="toor" +tty=0 +options=$(getopt -o hp:n:a:FcPv:t:S:u:w:sm: -l help,rootfs:,path:,name:,arch:,flush-cache,cache-only,private-portage,variant:,portage-dir:,tarball:,auth_key:,user:,autologin,password:,more-secure,mirror:,tty:,nettun -- "$@") + +eval set -- "$options" + +while true +do + case "$1" in + -h|--help) usage $0 && exit 0;; + --rootfs) rootfs=$2; shift 2;; + -p|--path) path=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -a|--arch) arch=$2; shift 2;; + -F|--flush-cache) flush_cache=1; shift 1;; + -c|--cache-only) cache_only=1; shitf 1;; + -P|--private-portage) private_portage=1; shift 1;; + -v|--variant) variant=$2; shift 2;; + --portage-dir) portage_dir=$2; shift 2;; + -t|--tarball) tarball=$2; shift 2;; + -S|--auth-key) auth_key=$2; shift 2;; + -u|--user) user=$2; shift 2;; + -w|--password) forced_password=1; password=$2; shift 2;; + -s|--more-secure) more_secure=1; shift 1;; + -m|--mirror) mirror=$2; shift 2;; + --nettun) nettun=1; shift 1;; + --tty) [[ $2 -lt 6 ]] && tty=$2; shift 2;; + --autologin) autologin=1; shift 1;; + --) shift 1; break ;; + *) break ;; + esac +done + +cacheroot="@LOCALSTATEDIR@/cache/lxc/gentoo" +portage_cache="${cacheroot}/portage.tbz" +cachefs="${cacheroot}/rootfs-${arch}-${variant}" + +alias wget="wget --timeout=8 --read-timeout=15 -c -t10 -nd" + +do_all() { + cache_setup + if [ -z "${cache_only}" ]; then + container_setup + fi +} + +execute_exclusively "cache-${arch}-${variant}" 60 do_all