#!/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 : ${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 add -U --initdb --root $rootfs $opt_arch "$@" alpine-base } configure_alpine() { rootfs="$1" echo "Setting up /etc/inittab" cat >"$rootfs"/etc/inittab< "$rootfs/etc/resolv.conf" # configure the network using the dhcp # note that lxc will set up lo interface cat < $rootfs/etc/network/interfaces #auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp EOF # set the hostname echo $hostname > $rootfs/etc/hostname # missing device nodes echo "Setting up device nodes" mkdir -p -m 755 "$rootfs/dev/pts" mkdir -p -m 1777 "$rootfs/dev/shm" mknod -m 666 "$rootfs/dev/full" c 1 7 mknod -m 666 "$rootfs/dev/random" c 1 8 mknod -m 666 "$rootfs/dev/urandom" c 1 9 mknod -m 666 "$rootfs/dev/tty0" c 4 0 mknod -m 666 "$rootfs/dev/tty1" c 4 1 mknod -m 666 "$rootfs/dev/tty2" c 4 2 mknod -m 666 "$rootfs/dev/tty3" c 4 3 mknod -m 666 "$rootfs/dev/tty4" c 4 4 # mknod -m 600 "$rootfs/dev/initctl" p mknod -m 666 "$rootfs/dev/tty" c 5 0 mknod -m 666 "$rootfs/dev/console" c 5 1 mknod -m 666 "$rootfs/dev/ptmx" c 5 2 # start services ln -s /etc/init.d/syslog "$rootfs"/etc/runlevels/default/syslog return 0 } copy_configuration() { path=$1 rootfs=$2 hostname=$3 grep -q "^lxc.rootfs" $path/config 2>/dev/null \ || echo "lxc.rootfs = $rootfs" >> $path/config if [ -n "$lxc_arch" ]; then echo "lxc.arch = $lxc_arch" >> $path/config fi lxc_network_link_line="# lxc.network.link = br0" for br in lxcbr0 virbr0 br0; do if [ -d /sys/class/net/$br/bridge ]; then lxc_network_link_line="lxc.network.link = $br" break fi done if ! grep -q "^lxc.network.type" $path/config 2>/dev/null; then cat <> $path/config lxc.network.type = veth $lxc_network_link_line lxc.network.flags = up EOF fi # if there is exactly one veth network entry, make sure it has an # associated mac address. nics=$(grep -e '^lxc\.network\.type[ \t]*=[ \t]*veth' $path/config | wc -l) if [ "$nics" -eq 1 ] && ! grep -q "^lxc.network.hwaddr" $path/config; then # see http://sourceforge.net/tracker/?func=detail&aid=3411497&group_id=163076&atid=826303 hwaddr="fe:$(dd if=/dev/urandom bs=8 count=1 2>/dev/null |od -t x8 | \ head -1 |awk '{print $2}' | cut -c1-10 |\ sed 's/\(..\)/\1:/g; s/.$//')" echo "lxc.network.hwaddr = $hwaddr" >> $path/config fi cat <> $path/config 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 # devices lxc.cgroup.devices.deny = a # /dev/null and zero lxc.cgroup.devices.allow = c 1:3 rwm lxc.cgroup.devices.allow = c 1:5 rwm # consoles lxc.cgroup.devices.allow = c 5:1 rwm lxc.cgroup.devices.allow = c 5:0 rwm lxc.cgroup.devices.allow = c 4:0 rwm lxc.cgroup.devices.allow = c 4:1 rwm # /dev/{,u}random lxc.cgroup.devices.allow = c 1:9 rwm 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 rm # mounts point lxc.mount.entry=proc proc proc nodev,noexec,nosuid 0 0 lxc.mount.entry=run run tmpfs nodev,noexec,nosuid,relatime,size=1m,mode=0755 0 0 lxc.mount.entry=none dev/pts devpts gid=5,mode=620 0 0 EOF return 0 } die() { echo "$@" >&2 exit 1 } usage() { cat >&2 <] [-R|--release ] [-a|--arch ] [--rootfs ] -p|--path -n|--name [PKG...] EOF } usage_err() { usage exit 1 } optarg_check() { if [ -z "$2" ]; then usage_err "option '$1' requires an argument" fi } default_path=@LXCPATH@ release= arch=$(uname -m) while [ $# -gt 0 ]; do opt="$1" shift case "$opt" in -h|--help) usage exit 0 ;; -n|--name) optarg_check $opt "$1" name=$1 shift ;; --rootfs) optarg_check $opt "$1" rootfs=$1 shift ;; -p|--path) optarg_check $opt "$1" path=$1 shift ;; -r|--repository) optarg_check $opt "$1" repository=$1 shift ;; -R|--release) optarg_check $opt "$1" release=$1 shift ;; -a|--arch) optarg_check $opt "$1" arch=$1 shift ;; --) break;; --*=*) # split --myopt=foo=bar into --myopt foo=bar set -- ${opt%=*} ${opt#*=} "$@" ;; -?) usage_err "unknown option '$opt'" ;; -*) # split opts -abc into -a -b -c set -- $(echo "${opt#-}" | sed 's/\(.\)/ -\1/g') "$@" ;; esac done [ -z "$name" ] && usage_err if [ -z "${path}" ]; then path="${default_path}/${name}" fi if [ -z "$rootfs" ]; then 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 apk_arch=$arch case "$arch" in i[3-6]86) apk_arch=x86;; x86) lxc_arch=i686;; x86_64|"") ;; *) 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"