lxc-abyss (8916B)
- #!/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