mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-07-13 23:12:50 +00:00

Create a tty so we get login prompt on console by default Signed-off-by: Natanael Copa <ncopa@alpinelinux.org> Acked-by: Stéphane Graber <stgraber@ubuntu.com>
389 lines
11 KiB
Bash
389 lines
11 KiB
Bash
#!/bin/sh
|
|
|
|
# Detect use under userns (unsupported)
|
|
for arg in "$@"; do
|
|
[ "$arg" = "--" ] && break
|
|
if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then
|
|
echo "This template can't be used for unprivileged containers." 1>&2
|
|
echo "You may want to try the \"download\" template instead." 1>&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# Make sure the usual locations are in PATH
|
|
PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin
|
|
export PATH
|
|
|
|
key_sha256sums="9c102bcc376af1498d549b77bdbfa815ae86faa1d2d82f040e616b18ef2df2d4 alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub
|
|
2adcf7ce224f476330b5360ca5edb92fd0bf91c92d83292ed028d7c4e26333ab alpine-devel@lists.alpinelinux.org-4d07755e.rsa.pub
|
|
ebf31683b56410ecc4c00acd9f6e2839e237a3b62b5ae7ef686705c7ba0396a9 alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub
|
|
1bb2a846c0ea4ca9d0e7862f970863857fc33c32f5506098c636a62a726a847b alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub
|
|
12f899e55a7691225603d6fb3324940fc51cd7f133e7ead788663c2b7eecb00c alpine-devel@lists.alpinelinux.org-5261cecb.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)
|
|
random=$(hexdump -n 2 -e '/2 "%u"' /dev/urandom)
|
|
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<<EOF
|
|
::sysinit:/sbin/rc sysinit
|
|
::wait:/sbin/rc default
|
|
console:12345:respawn:/sbin/getty 38400 console
|
|
tty1:12345:respawn:/sbin/getty 38400 tty1
|
|
tty2:12345:respawn:/sbin/getty 38400 tty2
|
|
tty3:12345:respawn:/sbin/getty 38400 tty3
|
|
tty4:12345:respawn:/sbin/getty 38400 tty4
|
|
::ctrlaltdel:/sbin/reboot
|
|
::shutdown:/sbin/rc shutdown
|
|
EOF
|
|
# set up timezone
|
|
if [ -f /etc/TZ ]; then
|
|
cp /etc/TZ "$rootfs/etc/TZ"
|
|
fi
|
|
|
|
# set up nameserver
|
|
grep nameserver /etc/resolv.conf > "$rootfs/etc/resolv.conf"
|
|
|
|
# configure the network using the dhcp
|
|
cat <<EOF > $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/zero" c 1 5
|
|
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/bootmisc "$rootfs"/etc/runlevels/boot/bootmisc
|
|
ln -s /etc/init.d/syslog "$rootfs"/etc/runlevels/boot/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 <<EOF >> $path/config
|
|
lxc.network.type = veth
|
|
$lxc_network_link_line
|
|
lxc.network.flags = up
|
|
EOF
|
|
fi
|
|
|
|
# if there is exactly one veth or macvlan network entry, make sure
|
|
# it has an associated mac address.
|
|
nics=$(awk -F '[ \t]*=[ \t]*' \
|
|
'$1=="lxc.network.type" && ($2=="veth" || $2=="macvlan") {print $2}' \
|
|
$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 -n 1 |awk '{print $2}' | cut -c1-10 |\
|
|
sed 's/\(..\)/\1:/g; s/.$//')"
|
|
echo "lxc.network.hwaddr = $hwaddr" >> $path/config
|
|
fi
|
|
|
|
cat <<EOF >> $path/config
|
|
|
|
lxc.tty = 4
|
|
lxc.pts = 1024
|
|
lxc.utsname = $hostname
|
|
lxc.cap.drop = sys_module mac_admin mac_override sys_time sys_admin
|
|
|
|
# When using LXC with apparmor, uncomment the next line to run unconfined:
|
|
#lxc.aa_profile = unconfined
|
|
|
|
# devices
|
|
lxc.cgroup.devices.deny = a
|
|
# /dev/null, zero and full
|
|
lxc.cgroup.devices.allow = c 1:3 rwm
|
|
lxc.cgroup.devices.allow = c 1:5 rwm
|
|
lxc.cgroup.devices.allow = c 1:7 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
|
|
lxc.mount.entry=shm dev/shm tmpfs nodev,nosuid,noexec,mode=1777 0 0
|
|
|
|
EOF
|
|
|
|
return 0
|
|
}
|
|
|
|
die() {
|
|
echo "$@" >&2
|
|
exit 1
|
|
}
|
|
|
|
usage() {
|
|
cat >&2 <<EOF
|
|
Usage: $(basename $0) [-h|--help] [-r|--repository <url>]
|
|
[-R|--release <release>] [-a|--arch <arch>]
|
|
[--rootfs <rootfs>] -p|--path <path> -n|--name <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)
|
|
|
|
# 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"
|
|
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
|
|
lxc_arch=x86
|
|
;;
|
|
x86)
|
|
lxc_arch=i686
|
|
;;
|
|
x86_64|"")
|
|
;;
|
|
arm*)
|
|
apk_arch=armhf
|
|
;;
|
|
*)
|
|
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"
|