diff --git a/templates/lxc-alpine.in b/templates/lxc-alpine.in index 962d274..ce7226f 100644 --- a/templates/lxc-alpine.in +++ b/templates/lxc-alpine.in @@ -1,20 +1,99 @@ #!/bin/sh +key_sha256sums="9c102bcc376af1498d549b77bdbfa815ae86faa1d2d82f040e616b18ef2df2d4 alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub +2adcf7ce224f476330b5360ca5edb92fd0bf91c92d83292ed028d7c4e26333ab alpine-devel@lists.alpinelinux.org-4d07755e.rsa.pub" + +get_static_apk () { + wget="wget -q -O -" + pkglist=alpine-keys:apk-tools-static + auto_repo_dir= + + if [ -z "$repository" ]; then + url=http://wiki.alpinelinux.org/cgi-bin/dl.cgi + if [ -z "$release" ]; then + echo -n "Determining the latest release... " + release=$($wget $url/.latest.$apk_arch.txt | \ + cut -d " " -f 3 | cut -d / -f 1 | uniq) + if [ -z "$release" ]; then + echo failed + return 1 + fi + echo $release + fi + auto_repo_dir=$release/main + repository=$url/$auto_repo_dir + pkglist=$pkglist:alpine-mirrors + fi + + rootfs="$1" + echo "Using static apk from $repository/$apk_arch" + wget="$wget $repository/$apk_arch" + + # parse APKINDEX to find the current versions + static_pkgs=$($wget/APKINDEX.tar.gz | \ + tar -Oxz APKINDEX | \ + awk -F: -v pkglist=$pkglist ' + BEGIN { split(pkglist,pkg) } + $0 != "" { f[$1] = $2 } + $0 == "" { for (i in pkg) + if (pkg[i] == f["P"]) + print(f["P"] "-" f["V"] ".apk") }') + [ "$static_pkgs" ] || return 1 + + mkdir -p "$rootfs" || return 1 + for pkg in $static_pkgs; do + echo "Downloading $pkg" + $wget/$pkg | tar -xz -C "$rootfs" + done + + # clean up .apk meta files + rm -f "$rootfs"/.[A-Z]* + + # verify checksum of the key + keyname=$(echo $rootfs/sbin/apk.static.*.pub | sed 's/.*\.SIGN\.RSA\.//') + checksum=$(echo "$key_sha256sums" | grep -w "$keyname") + if [ -z "$checksum" ]; then + echo "ERROR: checksum is missing for $keyname" + return 1 + fi + (cd $rootfs/etc/apk/keys && echo "$checksum" | sha256sum -c -) || return 1 + + # verify the static apk binary signature + APK=$rootfs/sbin/apk.static + openssl dgst -verify $rootfs/etc/apk/keys/$keyname \ + -signature "$APK.SIGN.RSA.$keyname" "$APK" || return 1 + + if [ "$auto_repo_dir" ]; then + mirror_list=$rootfs/usr/share/alpine-mirrors/MIRRORS.txt + mirror_count=$(wc -l $mirror_list | cut -d " " -f 1) + repository=$(sed $(expr $RANDOM % $mirror_count + 1)\!d \ + $mirror_list)$auto_repo_dir + echo "Selecting mirror $repository" + fi +} + install_alpine() { rootfs="$1" shift mkdir -p "$rootfs"/etc/apk || return 1 - cp -r ${keys_dir:-/etc/apk/keys} "$rootfs"/etc/apk/ + : ${keys_dir:=/etc/apk/keys} + if ! [ -d "$rootfs"/etc/apk/keys ] && [ -d "$keys_dir" ]; then + cp -r "$keys_dir" "$rootfs"/etc/apk/keys + fi if [ -n "$repository" ]; then echo "$repository" > "$rootfs"/etc/apk/repositories else cp /etc/apk/repositories "$rootfs"/etc/apk/repositories || return 1 + if [ -n "$release" ]; then + sed -i -e "s:/[^/]\+/\([^/]\+\)$:/$release/\1:" \ + "$rootfs"/etc/apk/repositories + fi fi opt_arch= if [ -n "$apk_arch" ]; then opt_arch="--arch $apk_arch" fi - ${APK:-apk} add -U --initdb --root $rootfs $opt_arch "$@" alpine-base + $APK add -U --initdb --root $rootfs $opt_arch "$@" alpine-base } configure_alpine() { @@ -109,6 +188,7 @@ EOF lxc.tty = 4 lxc.pts = 1024 lxc.utsname = $hostname +lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined @@ -129,7 +209,7 @@ lxc.cgroup.devices.allow = c 1:8 rwm lxc.cgroup.devices.allow = c 136:* rwm lxc.cgroup.devices.allow = c 5:2 rwm # rtc -lxc.cgroup.devices.allow = c 254:0 rwm +lxc.cgroup.devices.allow = c 254:0 rm # mounts point lxc.mount.entry=proc proc proc nodev,noexec,nosuid 0 0 @@ -148,8 +228,10 @@ die() { usage() { cat >&2 <] [-a|--arch ] - -p|--path -n|--name [PKG...] +Usage: $(basename $0) [-h|--help] [-r|--repository ] + [-R|--release ] [-a|--arch ] + [--rootfs ] -p|--path -n|--name + [PKG...] EOF } @@ -165,6 +247,14 @@ optarg_check() { } default_path=@LXCPATH@ +release= +arch=$(uname -m) + +# template mknods, requires root +if [ $(id -u) -ne 0 ]; then + echo "$(basename $0): must be run as root" >&2 + exit 1 +fi while [ $# -gt 0 ]; do opt="$1" @@ -179,6 +269,11 @@ while [ $# -gt 0 ]; do name=$1 shift ;; + --rootfs) + optarg_check $opt "$1" + rootfs=$1 + shift + ;; -p|--path) optarg_check $opt "$1" path=$1 @@ -189,6 +284,11 @@ while [ $# -gt 0 ]; do repository=$1 shift ;; + -R|--release) + optarg_check $opt "$1" + release=$1 + shift + ;; -a|--arch) optarg_check $opt "$1" arch=$1 @@ -217,9 +317,11 @@ if [ -z "${path}" ]; then path="${default_path}/${name}" fi -rootfs=`awk -F= '$1 ~ /^lxc.rootfs/ { print $2 }' "$path/config" 2>/dev/null` if [ -z "$rootfs" ]; then - rootfs="${path}/rootfs" + rootfs=`awk -F= '$1 ~ /^lxc.rootfs/ { print $2 }' "$path/config" 2>/dev/null` + if [ -z "$rootfs" ]; then + rootfs="${path}/rootfs" + fi fi lxc_arch=$arch @@ -234,6 +336,11 @@ case "$arch" in *) die "unsupported architecture: $arch";; esac +: ${APK:=apk} +if ! which $APK >/dev/null; then + get_static_apk "$rootfs" || die "Failed to download a valid static apk" +fi + install_alpine "$rootfs" "$@" || die "Failed to install rootfs for $name" configure_alpine "$rootfs" "$name" || die "Failed to configure $name" copy_configuration "$path" "$rootfs" "$name"