]>
git.proxmox.com Git - mirror_lxc.git/blob - templates/lxc-alpine.in
4 # Exit on error and treat unset variables as an error.
8 # LXC template for Alpine Linux 3+
11 # Note: Do not replace tabs with spaces, it would break heredocs!
14 # Jakub Jirutka <jakub@jirutka.cz>
16 # This library is free software; you can redistribute it and/or
17 # modify it under the terms of the GNU Lesser General Public
18 # License as published by the Free Software Foundation; either
19 # version 2.1 of the License, or (at your option) any later version.
21 # This library is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 # Lesser General Public License for more details.
26 # You should have received a copy of the GNU Lesser General Public
27 # License along with this library; if not, write to the Free Software
28 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
31 #=========================== Constants ============================#
33 # Make sure the usual locations are in PATH
34 export PATH
=$PATH:/usr
/sbin
:/usr
/bin
:/sbin
:/bin
36 readonly LOCAL_STATE_DIR
='@LOCALSTATEDIR@'
37 readonly LXC_TEMPLATE_CONFIG
='@LXCTEMPLATECONFIG@'
38 readonly LXC_CACHE_DIR
="${LXC_CACHE_PATH:-"$LOCAL_STATE_DIR/cache/lxc"}/alpine"
40 # SHA256 checksums of GPG keys for APK.
41 readonly APK_KEYS_SHA256
="\
42 9c102bcc376af1498d549b77bdbfa815ae86faa1d2d82f040e616b18ef2df2d4 alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub
43 2adcf7ce224f476330b5360ca5edb92fd0bf91c92d83292ed028d7c4e26333ab alpine-devel@lists.alpinelinux.org-4d07755e.rsa.pub
44 ebf31683b56410ecc4c00acd9f6e2839e237a3b62b5ae7ef686705c7ba0396a9 alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub
45 1bb2a846c0ea4ca9d0e7862f970863857fc33c32f5506098c636a62a726a847b alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub
46 12f899e55a7691225603d6fb3324940fc51cd7f133e7ead788663c2b7eecb00c alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub
47 73867d92083f2f8ab899a26ccda7ef63dfaa0032a938620eda605558958a8041 alpine-devel@lists.alpinelinux.org-58199dcc.rsa.pub
48 9a4cd858d9710963848e6d5f555325dc199d1c952b01cf6e64da2c15deedbd97 alpine-devel@lists.alpinelinux.org-58cbb476.rsa.pub
49 780b3ed41786772cbc7b68136546fa3f897f28a23b30c72dde6225319c44cfff alpine-devel@lists.alpinelinux.org-58e4f17d.rsa.pub"
51 readonly APK_KEYS_URI
='http://alpinelinux.org/keys'
52 readonly DEFAULT_MIRROR_URL
='http://dl-cdn.alpinelinux.org/alpine'
54 : ${APK_KEYS_DIR:=/etc/apk/keys}
55 if ! ls "$APK_KEYS_DIR"/alpine
* >/dev
/null
2>&1; then
56 APK_KEYS_DIR
="$LXC_CACHE_DIR/bootstrap/keys"
60 : ${APK:=$(command -v apk || true)}
61 if [ ! -x "$APK" ]; then
62 APK
="$LXC_CACHE_DIR/bootstrap/sbin/apk.static"
67 #======================== Helper Functions ========================#
71 Template specific options can be passed to lxc-create after a '--' like this:
73 lxc-create --name=NAME [lxc-create-options] -- [template-options] [PKG...]
75 PKG Additional APK package(s) to install into the container.
78 -a ARCH, --arch=ARCH The container architecture (e.g. x86, x86_64); defaults
80 -d, --debug Run this script in a debug mode (set -x and wget w/o -q).
81 -F, --flush-cache Remove cached files before build.
82 -m URL --mirror=URL The Alpine mirror to use; defaults to $DEFAULT_MIRROR_URL.
83 -r VER, --release=VER The Alpine release branch to install; default is the
86 Environment variables:
87 APK The apk-tools binary to use when building rootfs. If not set
88 or not executable and apk is not on PATH, then the script
89 will download the latest apk-tools-static.
90 APK_KEYS_DIR Path to directory with GPG keys for APK. If not set and
91 /etc/apk/keys does not contain alpine keys, then the script
92 will download the keys from ${APK_KEYS_URI}.
93 LXC_CACHE_PATH Path to the cache directory where to store bootstrap files
99 local retval
=$1; shift
101 printf 'ERROR: %s\n' "$@" 1>&2
110 if [ "$DEBUG" = 'yes' ]; then
113 wget
-T 10 -O - -q $@
117 latest_release_branch
() {
119 local branch
=$
(fetch
"$MIRROR_URL/latest-stable/releases/$arch/latest-releases.yaml" \
120 |
sed -En 's/^[ \t]*branch: (.*)$/\1/p' \
122 [ -n "$branch" ] && echo "$branch"
127 x86 | i
[3-6]86) echo 'x86';;
128 x86_64 | amd64
) echo 'x86_64';;
129 aarch64 | arm64
) echo 'aarch64';;
130 armv7
) echo 'armv7';;
132 ppc64le
) echo 'ppc64le';;
142 mkdir
-p "$LOCAL_STATE_DIR/lock/subsys"
146 echo -n "Obtaining an exclusive lock..."
147 if ! flock
-x 9; then
154 } 9> "$LOCAL_STATE_DIR/lock/subsys/lxc-alpine-$lock_name"
160 #============================ Bootstrap ===========================#
163 if [ "$FLUSH_CACHE" = 'yes' ] && [ -d "$LXC_CACHE_DIR/bootstrap" ]; then
164 einfo
'Cleaning cached bootstrap files'
165 rm -Rf "$LXC_CACHE_DIR/bootstrap"
168 einfo
'Fetching and/or verifying APK keys'
169 fetch_apk_keys
"$APK_KEYS_DIR"
171 if [ ! -x "$APK" ]; then
172 einfo
'Fetching apk-tools static binary'
174 local host_arch
=$
(parse_arch $
(uname
-m))
175 fetch_apk_static
"$LXC_CACHE_DIR/bootstrap" "$host_arch"
186 echo "$APK_KEYS_SHA256" |
while read -r line
; do
187 keyname
="${line##* }"
188 if [ ! -s "$keyname" ]; then
189 fetch
"$APK_KEYS_URI/$keyname" > "$keyname"
191 echo "$line" | sha256sum
-c -
200 local pkg_name
='apk-tools-static'
204 local pkg_ver
=$
(fetch
"$MIRROR_URL/latest-stable/main/$arch/APKINDEX.tar.gz" \
205 |
tar -xzO APKINDEX \
206 |
sed -n "/P:${pkg_name}/,/^$/ s/V:\(.*\)$/\1/p")
208 [ -n "$pkg_ver" ] || die
2 "Cannot find a version of $pkg_name in APKINDEX"
210 fetch
"$MIRROR_URL/latest-stable/main/$arch/${pkg_name}-${pkg_ver}.apk" \
211 |
tar -xz -C "$dest" sbin
/ # --extract --gzip --directory
213 [ -s "$dest/sbin/apk.static" ] || die
2 'apk.static not found'
215 local keyname
=$
(echo "$dest"/sbin
/apk.static.
*.pub |
sed 's/.*\.SIGN\.RSA\.//')
217 -verify "$APK_KEYS_DIR/$keyname" \
218 -signature "$dest/sbin/apk.static.SIGN.RSA.$keyname" \
219 "$dest/sbin/apk.static" \
220 || die
2 'Signature verification for apk.static failed'
222 # Note: apk doesn't return 0 for --version
223 local out
="$("$dest"/sbin/apk.static --version)"
226 [ "${out%% *}" = 'apk-tools' ] || die
3 'apk.static --version failed'
230 #============================ Install ============================#
236 local extra_packages
="$4"
237 local apk_cache
="$LXC_CACHE_DIR/apk/$arch"
239 if [ "$FLUSH_CACHE" = 'yes' ] && [ -d "$apk_cache" ]; then
240 einfo
"Cleaning cached APK packages for $arch"
243 mkdir
-p "$apk_cache"
245 einfo
"Installing Alpine Linux in $dest"
249 ln -s "$apk_cache" etc
/apk
/cache
251 local repo
; for repo
in main community
; do
252 echo "$MIRROR_URL/$branch/$repo" >> etc
/apk
/repositories
255 install_packages
"$arch" "alpine-base $extra_packages"
263 || die
3 'Failed to execute /bin/true in chroot, the builded rootfs is broken!'
273 $APK --arch="$arch" --root=.
--keys-dir="$APK_KEYS_DIR" \
274 --update-cache --initdb add
$packages
278 mkdir
-p -m 755 dev
/pts
279 mkdir
-p -m 1777 dev
/shm
281 mknod
-m 666 dev
/zero c
1 5
282 mknod
-m 666 dev
/full c
1 7
283 mknod
-m 666 dev
/random c
1 8
284 mknod
-m 666 dev
/urandom c
1 9
286 local i
; for i
in $
(seq 0 4); do
287 mknod
-m 620 dev
/tty
$i c
4 $i
288 chown
0:5 dev
/tty
$i # root:tty
291 mknod
-m 666 dev
/tty c
5 0
292 chown
0:5 dev
/tty
# root:tty
293 mknod
-m 620 dev
/console c
5 1
294 mknod
-m 666 dev
/ptmx c
5 2
295 chown
0:5 dev
/ptmx
# root:tty
299 # Remove unwanted ttys.
300 sed -i '/^tty[5-9]\:\:.*$/d' etc
/inittab
302 cat <<-EOF >> etc/inittab
303 # Main LXC console console
304 ::respawn:/sbin/getty 38400 console
309 # This runscript injects localhost entries with the current hostname
311 cat <<'EOF' > etc/init.d/hosts
315 local start_tag='# begin generated'
316 local end_tag='# end generated'
320 $start_tag by /etc/init.d/hosts
321 127.0.0.1 $(hostname).local $(hostname) localhost
322 ::1 $(hostname).local $(hostname) localhost
327 if grep -q "^${start_tag}" /etc
/hosts
; then
328 # escape \n, busybox sed doesn't like them
329 content
=${content//$'\n'/\\$'\n'}
331 sed -ni "/^${start_tag}/ {
333 # read and discard next line and repeat until $end_tag or EOF
334 :a; n; /^${end_tag}/!ba; n
337 printf "$content" >> /etc
/hosts
341 chmod +x etc
/init.d
/hosts
343 # Wipe it, will be generated by the above runscript.
348 # Note: loopback is automatically started by LXC.
349 cat <<-EOF > etc/network/interfaces
352 hostname \$(hostname)
359 # Specify the LXC subsystem.
360 sed -i 's/^#*rc_sys=.*/rc_sys="lxc"/' etc
/rc.conf
363 for svc_name
in bootmisc hosts syslog
; do
364 ln -s /etc
/init.d
/$svc_name etc
/runlevels
/boot
/$svc_name
368 for svc_name
in networking cron crond
; do
369 # issue 1164: alpine renamed cron to crond
370 # Use the one that exists.
371 if [ -e etc
/init.d
/$svc_name ]; then
372 ln -s /etc
/init.d
/$svc_name etc
/runlevels
/default
/$svc_name
378 #=========================== Configure ===========================#
380 configure_container
() {
385 cat <<-EOF >> "$config"
387 # Specify container architecture.
391 lxc.uts.name = $hostname
393 # If something doesn't work, try to comment this out.
394 # Dropping sys_admin disables container root from doing a lot of things
395 # that could be bad like re-mounting lxc fstab entries rw for example,
396 # but also disables some useful things like being able to nfs mount, and
397 # things that are already namespaced with ns_capable() kernel checks, like
399 lxc.cap.drop = sys_admin
401 # Include common configuration.
402 lxc.include = $LXC_TEMPLATE_CONFIG/alpine.common.conf
407 #============================= Main ==============================#
409 if [ "$(id -u)" != "0" ]; then
410 die
1 "This script must be run as 'root'"
413 # Parse command options.
414 options
=$
(getopt
-o a
:dFm
:n
:p
:r
:h
-l arch
:,debug
,flush-cache
,mirror
:,name
:,\
415 path
:,release
:,rootfs
:,help,mapped-uid
:,mapped-gid
: -- "$@")
416 eval set -- "$options"
418 # Clean variables and set defaults.
428 # Process command options.
429 while [ $# -gt 0 ]; do
438 flush_cache
='yes'; shift 1
441 mirror_url
=$2; shift 2
462 die
1 "This template can't be used for unprivileged containers." \
463 'You may want to try the "download" template instead.'
466 echo "Unknown option: $1" 1>&2
474 [ "$debug" = 'yes' ] && set -x
476 # Set global variables.
477 readonly DEBUG
="$debug"
478 readonly FLUSH_CACHE
="$flush_cache"
479 readonly MIRROR_URL
="${mirror_url:-$DEFAULT_MIRROR_URL}"
482 [ -n "$name" ] || die
1 'Missing required option --name'
483 [ -n "$path" ] || die
1 'Missing required option --path'
485 if [ -z "$rootfs" ] && [ -f "$path/config" ]; then
486 rootfs
="$(sed -nE 's/^lxc.rootfs.path\s*=\s*(.*)$/\1/p' "$path/config
")"
488 if [ -z "$rootfs" ]; then
489 rootfs
="$path/rootfs"
492 arch
=$
(parse_arch
"$arch") \
493 || die
1 "Unsupported architecture: $arch"
495 if [ -z "$release" ]; then
496 release
=$
(latest_release_branch
"$arch") \
497 || die
2 'Failed to resolve Alpine last release branch'
501 run_exclusively
'bootstrap' 10 bootstrap
502 run_exclusively
"$arch" 30 install "$rootfs" "$arch" "$release" "$extra_packages"
503 configure_container
"$path/config" "$name" "$arch"
505 einfo
"Container's rootfs and config have been created"
507 Edit the config file $path/config to check/enable networking setup.
508 The installed system is preconfigured for a loopback and single network
509 interface configured via DHCP.
511 To start the container, run "lxc-start -n $name".
512 The root password is not set; to enter the container run "lxc-attach -n $name".