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:
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