From 11f88f10cd167b865e7dd9172352fb1028367f2b Mon Sep 17 00:00:00 2001 From: Geaaru Date: Sat, 28 Jan 2017 22:55:25 +0100 Subject: [PATCH] Add LXC template script of Sabayon OS Signed-off-by: Geaaru --- configure.ac | 1 + templates/Makefile.am | 3 +- templates/lxc-sabayon.in | 502 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 505 insertions(+), 1 deletion(-) create mode 100644 templates/lxc-sabayon.in diff --git a/configure.ac b/configure.ac index 9ebcd25c5..066b0c953 100644 --- a/configure.ac +++ b/configure.ac @@ -897,6 +897,7 @@ AC_CONFIG_FILES([ templates/lxc-ubuntu-cloud templates/lxc-sparclinux templates/lxc-voidlinux + templates/lxc-sabayon src/Makefile src/lxc/Makefile diff --git a/templates/Makefile.am b/templates/Makefile.am index c6c5ea53a..6d889c43f 100644 --- a/templates/Makefile.am +++ b/templates/Makefile.am @@ -21,4 +21,5 @@ templates_SCRIPTS = \ lxc-ubuntu \ lxc-ubuntu-cloud \ lxc-sparclinux \ - lxc-voidlinux + lxc-voidlinux \ + lxc-sabayon diff --git a/templates/lxc-sabayon.in b/templates/lxc-sabayon.in new file mode 100644 index 000000000..4c9adbcb2 --- /dev/null +++ b/templates/lxc-sabayon.in @@ -0,0 +1,502 @@ +#!/bin/sh +# vim: set ts=4 sw=4 expandtab + +# Exit on error and treat unset variables as an error. +set -eu + +# +# LXC template for Sabayon OS, based of Alpine script. +# + +# Authors: +# Geaaru + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +#=========================== Constants ============================# + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +readonly LOCAL_STATE_DIR='@LOCALSTATEDIR@' +readonly LXC_TEMPLATE_CONFIG='@LXCTEMPLATECONFIG@' + + +# Temporary static MIRROR LIST. I will get list from online path on the near future. +readonly MIRRORS_LIST=" +http://mirror.it.sabayon.org/ +http://dl.sabayon.org/ +http://ftp.kddilabs.jp/Linux/packages/sabayonlinux/ +ftp://ftp.klid.dk/sabayonlinux/ +http://ftp.fsn.hu/pub/linux/distributions/sabayon/ +http://ftp.cc.uoc.gr/mirrors/linux/SabayonLinux/ +http://ftp.rnl.ist.utl.pt/pub/sabayon/ +ftp://ftp.nluug.nl/pub/os/Linux/distr/sabayonlinux/ +http://ftp.surfnet.nl/pub/os/Linux/distr/sabayonlinux/ +http://mirror.internode.on.net/pub/sabayon/ +http://mirror.yandex.ru/sabayon/ +http://sabayon.c3sl.ufpr.br/ +http://mirror.umd.edu/sabayonlinux/ +http://mirror.clarkson.edu/sabayon/ +http://na.mirror.garr.it/mirrors/sabayonlinux/" + +#======================== Global variables ========================# + +# Clean variables and set defaults. +arch="$(uname -m)" +debug='no' +flush_cache='no' +mirror_url= +name= +path= +release="DAILY" +rootfs= +unprivileged=false +mapped_uid= +mapped_gid= + +#======================== Helper Functions ========================# + +usage() { + cat <<-EOF +Template specific options can be passed to lxc-create after a '--' like this: + + lxc-create --name=NAME [lxc-create-options] -- [template-options] + +Template options: + -a ARCH, --arch=ARCH The container architecture (e.g. x86_64, armv7); defaults + to the host arch. + -d, --debug Run this script in a debug mode (set -x and wget w/o -q). + -m URL --mirror=URL The Sabayon mirror to use; defaults to random mirror. + -u, --unprivileged Tuning of rootfs for unprivileged containers. + Are needed --mapped-gid and --mapped-uid options. + -r, --release Identify release to use. Default is DAILY. + --mapped-gid Group Id to use on unprivileged container + (based of value present on file /etc/subgid). + --mapped-uid User Id to use on unprivileged container + (based of value present on file /etc/subuid) + +Environment variables: + RELEASE Release version of Sabayon. Default is ${RELEASE}. +EOF +} + +random_mirror_url() { + local url="" + + if [ $arch == 'amd64' ] ; then + url=$(echo $MIRRORS_LIST | sed -e 's/ /\n/g' | sort -R --random-source=/dev/urandom | head -n 1) + else + if [ $arch == 'armv7l' ] ; then + # Currently armv7l tarball is not on sabayon mirrored tree. + url="https://dockerbuilder.sabayon.org/" + fi + fi + + [ -n "$url" ] && echo "$url" +} + +die() { + local retval=$1; shift + + echo -e "==> $@\n" + exit $retval +} + +einfo() { + echo -e "==> $@\n" +} + +fetch() { + if [ "$debug" = 'yes' ]; then + wget -T 10 -O - $@ + else + wget -T 10 -O - -q $@ + fi +} + +parse_arch() { + case "$1" in + x86_64 | amd64) echo 'amd64';; + armv7 | armv7l) echo 'armv7l';; + #arm*) echo 'armhf';; + *) return 1;; + esac +} + +run_exclusively() { + + local lock_name="$1" + local timeout=$2 + local method=$3 + shift 3 + + mkdir -p "$LOCAL_STATE_DIR/lock/subsys" + + local retval + { + echo -n "Obtaining an exclusive lock..." + if ! flock -x 9; then + echo ' failed.' + return 1 + fi + echo ' done' + + ${method} $@ + retval=$? + } 9> "$LOCAL_STATE_DIR/lock/subsys/lxc-sabayon-$lock_name" + + return $retval +} + +create_url () { + + local url="" + # Example of amd64 tarball url + # http://mirror.yandex.ru/sabayon/iso/daily/Sabayon_Linux_DAILY_amd64_tarball.tar.gz + + if [ $arch == 'amd64' ] ; then + + if [ $release = 'DAILY' ] ; then + url="${MIRROR_URL}iso/daily/Sabayon_Linux_DAILY_amd64_tarball.tar.gz" + else + url="${MIRROR_URL}iso/monthly/Sabayon_Linux_${release}_amd64_tarball.tar.gz" + fi + else + # https://dockerbuilder.sabayon.org/Sabayon_Linux_16_armv7l.tar.bz2 + if [ $arch == 'armv7l' ] ; then + + # Currently $arch tarball is not on sabayon mirrored tree. + url="${MIRROR_URL}Sabayon_Linux_16_armv7l.tar.bz2" + + fi + fi + + echo $url +} + + +#=========================== Configure ===========================# + +unprivileged_rootfs() { + + pushd ${rootfs}/etc/systemd/system + + # Disable systemd-journald-audit.socket because it seems that doesn't + # start correctly on unprivileged container + ln -s /dev/null systemd-journald-audit.socket + + # Disable systemd-remount-fs.service because on unprivileged container + # systemd can't remount filesystem + ln -s /dev/null systemd-remount-fs.service + + # Remove mount of FUSE Control File system + ln -s /dev/null sys-fs-fuse-connections.mount + + # Change execution of service systemd-sysctl to avoid errors. + mkdir systemd-sysctl.service.d + cat < systemd-sysctl.service.d/00gentoo.conf +[Service] +ExecStart= +ExecStart=/usr/lib/systemd/systemd-sysctl --prefix=/etc/sysctl.d/ +EOF + + # Disable mount of hugepages + ln -s /dev/null dev-hugepages.mount + + # Fix TERM variable for container console + mkdir container-getty\@0.service.d + cat < container-getty\@0.service.d/00gentoo.conf +[Service] +Environment=TERM= +Environment=TERM=linux +EOF + + + popd + + pushd ${rootfs} + + # Disable sabayon-anti-fork-bomb limits (already apply to lxc container manager) + sed -i -e 's/^*/#*/g' ./etc/security/limits.d/00-sabayon-anti-fork-bomb.conf || return 1 + sed -i -e 's/^root/#root/g' ./etc/security/limits.d/00-sabayon-anti-fork-bomb.conf || return 1 + + popd + + return 0 +} + +unprivileged_shift_owner () { + + # I use /usr/bin/fuidshift from LXD project. + + einfo "Executing: fuidshift ${rootfs} u:0:${mapped_uid}:65536 g:0:${mapped_gid}:65536 ..." + + fuidshift ${rootfs} u:0:${mapped_uid}:65536 g:0:${mapped_gid}:65536 || + die 1 "Error on change owners of ${rootfs} directory" + + einfo "Done." + + # Fix permission of container directory + chmod a+rx ${path} + + return 0 +} + +systemd_container_tuning () { + + # To avoid error on start systemd-tmpfiles-setup service + # it is needed clean journal directory + rm -rf ${rootfs}/var/log/journal/ + + # Remove LVM service. Normally not needed on container system. + rm -rf ${rootfs}/etc/systemd/system/sysinit.target.wants/lvm2-lvmetad.service + + return 0 +} + +configure_container() { + local config="$1" + local hostname="$2" + local arch="$3" + local privileged_options="" + local unprivileged_options="" + + if [[ $unprivileged && $unprivileged == true ]] ; then + unprivileged_options=" +lxc.mount.auto = proc:mixed sys:mixed cgroup:mixed + +# Enable tty console for lxc-console command +lxc.tty = 1 + +lxc.id_map = u 0 ${mapped_uid} 65536 +lxc.id_map = g 0 ${mapped_gid} 65536 +" + + else + privileged_options=" +lxc.mount.auto = proc:mixed sys:mixed cgroup:mixed +lxc.cgroup.devices.deny = a +lxc.cgroup.devices.allow = b *:* m +lxc.cgroup.devices.allow = c *:* m +lxc.cgroup.devices.allow = c 136:* rwm +lxc.cgroup.devices.allow = c 1:3 rwm +lxc.cgroup.devices.allow = c 1:5 rwm +lxc.cgroup.devices.allow = c 1:7 rwm +lxc.cgroup.devices.allow = c 1:8 rwm +lxc.cgroup.devices.allow = c 1:9 rwm +lxc.cgroup.devices.allow = c 5:0 rwm +lxc.cgroup.devices.allow = c 5:1 rwm +lxc.cgroup.devices.allow = c 5:2 rwm +lxc.cgroup.devices.allow = c 10:229 rwm +" + fi + + cat <<-EOF >> "$config" +# Specify container architecture. +lxc.arch = $arch + +# Set hostname. +lxc.utsname = $hostname + +# If something doesn't work, try to comment this out. +# Dropping sys_admin disables container root from doing a lot of things +# that could be bad like re-mounting lxc fstab entries rw for example, +# but also disables some useful things like being able to nfs mount, and +# things that are already namespaced with ns_capable() kernel checks, like +# hostname(1). +lxc.cap.drop = sys_time sys_module sys_rawio mac_admin mac_override +#lxc.cap.drop = sys_admin + +lxc.autodev = 1 +lxc.pts = 1024 + +$unprivileged_options +$privileged_options + +# Customize lxc options through common directory +lxc.include = /usr/share/lxc/config/common.conf.d/ + +EOF +} + + +#============================= Main ==============================# + +parse_cmdline() { + + # Parse command options. + local short_options="a:dm:n:p:r:hu" + local long_options="arch:,debug,mirror:,name:,path:,release:,rootfs:,mapped-uid:,mapped-gid:,help" + + options=$(getopt -u -q -a -o "$short_options" -l "$long_options" -- "$@") + + eval set -- "$options" + + # Process command options. + while [ $# -gt 0 ]; do + case $1 in + -a | --arch) + arch=$2 + shift + ;; + -d | --debug) + debug='yes' + ;; + -m | --mirror) + mirror_url=$2 + shift + ;; + -n | --name) + name=$2 + shift + ;; + -p | --path) + path=$2 + shift + ;; + -r | --release) + release=$2 + shift + ;; + --rootfs) + rootfs=$2 + shift + ;; + -u | --unprivileged) + unprivileged=true + ;; + -h | --help) + usage + exit 1 + ;; + --mapped-uid) + mapped_uid=$2 + shift + ;; + --mapped-gid) + mapped_gid=$2 + shift + ;; + --) + break + ;; + *) + einfo "Unknown option: $1" + usage + exit 1 + ;; + esac + shift + done + + if [ "$(id -u)" != "0" ]; then + die 1 "This script must be run as 'root'" + fi + + # Validate options. + [ -n "$name" ] || die 1 'Missing required option --name' + [ -n "$path" ] || die 1 'Missing required option --path' + + if [ -z "$rootfs" ] && [ -f "$path/config" ]; then + rootfs="$(sed -nE 's/^lxc.rootfs\s*=\s*(.*)$/\1/p' "$path/config")" + fi + if [ -z "$rootfs" ]; then + rootfs="$path/rootfs" + fi + + [ -z "$path" ] && die 1 "'path' parameter is required." + + arch=$(parse_arch "$arch") \ + || die 1 "Unsupported architecture: $arch" + + [[ $unprivileged && $unprivileged == true && -z "$mapped_uid" ]] && \ + die 1 'Missing required option --mapped-uid with --unprivileged option' + + [[ $unprivileged && $unprivileged == true && -z "$mapped_gid" ]] && \ + die 1 'Missing required option --mapped-gid with --unprivileged option' + + return 0 +} + +main () { + + local tarball="" + + # Set global variables. + RELEASE="${RELEASE:-"DAILY"}" + ARCH="${ARCH:-`uname -m`}" + OS="${OS:-"sabayon"}" + + einfo "Processing command line arguments: $@" + + # Parse command line options + parse_cmdline "$@" + + DEBUG="$debug" + MIRROR_URL="${mirror_url:-$(random_mirror_url)}" + + einfo "Use arch = $arch, mirror_url = $MIRROR_URL, path = $path, name = $name, release = $release, unprivileged = $unprivileged, rootfs = $rootfs, mapped_uid = $mapped_uid, mapped_gid = $mapped_gid" + + [ "$debug" = 'yes' ] && set -x + + # Download sabayon tarball + tarball=$(create_url) + einfo "Fetching tarball $tarball..." + + # TODO: use only a compression mode + if [ $arch == 'amd64' ] ; then + fetch "${tarball}" | tar -xpz -C "${rootfs}" + else + if [ $arch == 'armv7l' ] ; then + fetch "${tarball}" | tar -xpj -C "${rootfs}" + fi + fi + + einfo "Tarball ${tarball} Extracted." + + systemd_container_tuning + + # Fix container for unprivileged mode. + if [[ $unprivileged && $unprivileged == true ]] ; then + unprivileged_rootfs + unprivileged_shift_owner + fi + + return 0 +} + + +einfo "Prepare creation of sabayon container with params: $@ ($#)" + +# Here we go! +run_exclusively 'main' 10 main "$@" +configure_container "$path/config" "$name" "$arch" + +einfo "Container's rootfs and config have been created" +cat <<-EOF + Edit the config file $path/config to check/enable networking setup. + The installed system is preconfigured for a loopback and single network + interface configured via DHCP. + + To start the container, run "lxc-start -n $name". + The root password is not set; to enter the container run "lxc-attach -n $name". + + Note: From kenel >= 4.6 for use unprivileged containers it is needed this mount on host: + + mkdir /sys/fs/cgroup/systemd + mount -t cgroup -o none,name=systemd systemd /sys/fs/cgroup/systemd +EOF