logo

scripts

A bunch of scripts, some to be moved to their own repository
commit: 9c76ee27e93c2736c46289f38094bfd0b879168c
parent: 5f8fba6038bd374591b20faf00a1501762978a7d
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Sat, 16 Nov 2019 17:16:52 +0100

lxc-abyss: New LXC Template

Diffstat:

Alxc-templates/lxc-abyss348+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 348 insertions(+), 0 deletions(-)

diff --git a/lxc-templates/lxc-abyss b/lxc-templates/lxc-abyss @@ -0,0 +1,348 @@ +#!/bin/sh +# vim: set ts=4: + +# Exit on error and treat unset variables as an error. +set -eu + +# +# LXC template for Abyss Linux, based on Alpine one +# + +# Note: Do not replace tabs with spaces, it would break heredocs! + +# Authors: +# Jakub Jirutka <jakub@jirutka.cz> +# Haelwenn (lanodan) Monnier <contact+abyss@hacktivis.me> + +# 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='/var/lib' +readonly LXC_TEMPLATE_CONFIG='/usr/share/lxc/config' +#readonly LXC_CACHE_DIR="${LXC_CACHE_PATH:-"$LOCAL_STATE_DIR/cache/lxc"}/abyss" +readonly LXC_CACHE_DIR="/var/cache/apk" + +readonly DEFAULT_MIRROR_URL='https://mirror.getabyss.com/abyss' + +: ${APK:=$(command -v apk || true)} +if [ ! -x "$APK" ]; then + APK="$LXC_CACHE_DIR/bootstrap/usr/bin/apk" +fi +readonly APK + + +#======================== 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] [PKG...] + + PKG Additional APK package(s) to install into the container. + + Template options: + -a ARCH, --arch=ARCH The container architecture (e.g. x86, x86_64); 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 Abyss mirror to use; defaults to $DEFAULT_MIRROR_URL. + + Environment variables: + APK The apk-tools binary to use when building rootfs. If not set + or not executable and apk is not on PATH, then the script + will download the latest apk-tools. + LXC_CACHE_PATH Path to the cache directory where to store bootstrap files + and APK packages. + EOF +} + +die() { + local retval=$1; shift + + printf 'ERROR: %s\n' "$@" 1>&2 + exit $retval +} + +einfo() { + printf "\n==> $1\n" +} + +fetch() { + if [ "$DEBUG" = 'yes' ]; then + wget -T 10 -O - $@ + else + wget -T 10 -O - -q $@ + fi +} + +parse_arch() { + case "$1" in + x86 | i[3-6]86) echo 'x86';; + x86_64 | amd64) echo 'x86_64';; + aarch64 | arm64) echo 'aarch64';; + armv7) echo 'armv7';; + arm*) echo 'armhf';; + ppc64le) echo 'ppc64le';; + *) return 1;; + esac +} + +run_exclusively() { + local lock_name="$1" + local timeout=$2 + shift 2 + + 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' + + "$@"; retval=$? + } 9> "$LOCAL_STATE_DIR/lock/subsys/lxc-alpine-$lock_name" + + return $retval +} + + +#============================ Bootstrap ===========================# + +bootstrap() { + if [ ! -x "$APK" ]; then + einfo 'Fetching apk-tools static binary' + + local host_arch=$(parse_arch $(uname -m)) + fetch_apk_static "$LXC_CACHE_DIR/bootstrap" "$host_arch" + fi +} + +fetch_apk_static() { + local dest="$1" + local arch="$2" + local pkg_name='apk-tools' + + mkdir -p "$dest" + + local pkg_ver=$(fetch "$MIRROR_URL/core/$arch/APKINDEX.tar.gz" \ + | tar -xzO APKINDEX \ + | sed -n "/P:${pkg_name}/,/^$/ s/V:\(.*\)$/\1/p") + + [ -n "$pkg_ver" ] || die 2 "Cannot find a version of $pkg_name in APKINDEX" + + fetch "$MIRROR_URL/core/$arch/${pkg_name}-${pkg_ver}.apk" \ + | tar -xz -C "$dest" usr/bin/ # --extract --gzip --directory + + [ -s "$dest/usr/bin/apk" ] || die 2 'apk not found' + + # Note: apk doesn't return 0 for --version + local out="$("$dest"/usr/bin/apk --version)" + echo "$out" + + [ "${out%% *}" = 'apk-tools' ] || die 3 'apk --version failed' +} + + +#============================ Install ============================# + +install() { + local dest="$1" + local arch="$2" + local extra_packages="$3" + + einfo "Installing Abyss Linux in $dest" + cd "$dest" + + mkdir -p usr/bin usr/lib + ln -s bin usr/sbin + ln -s usr/bin bin + ln -s usr/bin sbin + ln -s usr/lib lib + + mkdir -p etc/apk + ln -s /var/cache/apk etc/apk/cache + + install_packages "$arch" "$dest" "filesystem" + install_packages "$arch" "$dest" "abyss-keyring ca-certificates apk-tools busybox openrc" + install_packages "$arch" "$dest" "$extra_packages" + busybox --install -s ./usr/bin + + $APK -X "$MIRROR_URL/core" --update-cache --allow-untrusted --arch="$arch" --root="$dest" fix || true + + echo "$MIRROR_URL/core" >> etc/apk/repositories + + # Busybox has getty but /etc/init.d/agetty is configured for agetty + # which is provided by util-linux + install_packages "$arch" "$dest" "util-linux" + ln -sf openrc-init ./usr/bin/init + for i in 1 2 3 4 5 6 + do + ln -s agetty ./etc/init.d/agetty.tty"$i" + ln -s /etc/init.d/agetty.tty"$i" ./etc/runlevels/boot/ + done + + chroot . /bin/true \ + || die 3 'Failed to execute /bin/true in chroot, the builded rootfs is broken!' + + rm etc/apk/cache + + cd - >/dev/null +} + +install_packages() { + local arch="$1" + local dest="$2" + local packages="$3" + + $APK -v -X "$MIRROR_URL/core" --update-cache --allow-untrusted --arch="$arch" --root="$dest" \ + --initdb add $packages || true +} + +#=========================== Configure ===========================# + +configure_container() { + local config="$1" + local hostname="$2" + local arch="$3" + + cat <<-EOF >> "$config" + + # Specify container architecture. + lxc.arch = $arch + + # Set hostname. + lxc.uts.name = $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_admin + + # Comment this out if you have to debug processes by tracing. + lxc.cap.drop = sys_ptrace + + # Comment this out if required by your applications. + lxc.cap.drop = setpcap + + # Include common configuration. + lxc.include = $LXC_TEMPLATE_CONFIG/alpine.common.conf + EOF +} + + +#============================= Main ==============================# + +if [ "$(id -u)" != "0" ]; then + die 1 "This script must be run as 'root'" +fi + +# Parse command options. +options=$(getopt -o a:dFm:n:p:r:h -l arch:,debug,mirror:,name:,\ +path:,release:,rootfs:,help,mapped-uid:,mapped-gid: -- "$@") +eval set -- "$options" + +# Clean variables and set defaults. +arch="$(uname -m)" +debug='no' +mirror_url= +name= +path= +rootfs= + +# Process command options. +while [ $# -gt 0 ]; do + case $1 in + -a | --arch) + arch=$2; shift 2 + ;; + -d | --debug) + debug='yes'; shift 1 + ;; + -m | --mirror) + mirror_url=$2; shift 2 + ;; + -n | --name) + name=$2; shift 2 + ;; + -p | --path) + path=$2; shift 2 + ;; + --rootfs) + rootfs=$2; shift 2 + ;; + -h | --help) + usage; exit 0 + ;; + --) + shift; break + ;; + --mapped-[ug]id) + die 1 "This template can't be used for unprivileged containers." \ + 'You may want to try the "download" template instead.' + ;; + *) + echo "Unknown option: $1" 1>&2 + usage; exit 1 + ;; + esac +done + +extra_packages="$@" + +[ "$debug" = 'yes' ] && set -x + +# Set global variables. +readonly DEBUG="$debug" +readonly MIRROR_URL="${mirror_url:-$DEFAULT_MIRROR_URL}" + +# 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.path\s*=\s*(.*)$/\1/p' "$path/config")" +fi +if [ -z "$rootfs" ]; then + rootfs="$path/rootfs" +fi + +arch=$(parse_arch "$arch") \ + || die 1 "Unsupported architecture: $arch" + +# Here we go! +run_exclusively 'bootstrap' 10 bootstrap +run_exclusively "$arch" 30 install "$rootfs" "$arch" "$extra_packages" +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 isn't configured for any networking setup, but lxc.network.type="none" (no additionnal network, reuse the host one) works out of the box. + + To start the container, run "lxc-start -n $name". + As root isn't locked with a password, you can already enter via "lxc-console -n $name". +EOF