]> git.proxmox.com Git - mirror_lxc.git/blame - templates/lxc-download.in
templates: Refuse to run unprivileged
[mirror_lxc.git] / templates / lxc-download.in
CommitLineData
71d3a659
SG
1#!/bin/sh
2
3# Client script for LXC container images.
4#
5# Copyright © 2014 Stéphane Graber <stgraber@ubuntu.com>
6#
7# This library is free software; you can redistribute it and/or
8# modify it under the terms of the GNU Lesser General Public
9# License as published by the Free Software Foundation; either
10# version 2.1 of the License, or (at your option) any later version.
11
12# This library is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15# Lesser General Public License for more details.
16
17# You should have received a copy of the GNU Lesser General Public
18# License along with this library; if not, write to the Free Software
19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20# USA
21
22set -eu
23
24LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@"
25LXC_HOOK_DIR="@LXCHOOKDIR@"
26LOCALSTATEDIR="@LOCALSTATEDIR@"
27
28# Defaults
29DOWNLOAD_DIST=
30DOWNLOAD_RELEASE=
31DOWNLOAD_ARCH=
32DOWNLOAD_VARIANT="default"
33DOWNLOAD_SERVER="images.linuxcontainers.org"
34DOWNLOAD_KEYID="0xBAEFF88C22F6E216"
35DOWNLOAD_KEYSERVER="pool.sks-keyservers.net"
36DOWNLOAD_VALIDATE="true"
37DOWNLOAD_FLUSH_CACHE="false"
41670b35 38DOWNLOAD_FORCE_CACHE="false"
71d3a659
SG
39DOWNLOAD_MODE="system"
40DOWNLOAD_USE_CACHE="false"
41DOWNLOAD_URL=
42DOWNLOAD_SHOW_HTTP_WARNING="true"
43DOWNLOAD_SHOW_GPG_WARNING="true"
b0f0932a 44DOWNLOAD_READY_GPG="false"
71d3a659 45DOWNLOAD_COMPAT_LEVEL=1
10a5fab6 46DOWNLOAD_LIST_IMAGES="false"
9accc2ef 47DOWNLOAD_BUILD=
b0f0932a 48DOWNLOAD_INTERACTIVE="false"
71d3a659
SG
49
50LXC_NAME=
51LXC_PATH=
52LXC_ROOTFS=
53LXC_MAPPED_UID=
54
55# Some useful functions
56cleanup() {
57 if [ -d "$DOWNLOAD_TEMP" ]; then
58 rm -Rf $DOWNLOAD_TEMP
59 fi
60}
61
62download_file() {
63 if ! wget -q https://${DOWNLOAD_SERVER}/$1 -O $2 >/dev/null 2>&1; then
64 if ! wget -q http://${DOWNLOAD_SERVER}/$1 -O $2 >/dev/null 2>&1; then
65 if [ "$3" = "noexit" ]; then
66 return 1
67 else
fad96766 68 echo "ERROR: Failed to download http://${DOWNLOAD_SERVER}/$1" 1>&2
71d3a659
SG
69 exit 1
70 fi
71 elif [ "$DOWNLOAD_SHOW_HTTP_WARNING" = "true" ]; then
72 DOWNLOAD_SHOW_HTTP_WARNING="false"
73 echo "WARNING: Failed to download the file over HTTPs." 1>&2
74 echo -n " The file was instead download over HTTP. " 1>&2
75 echo "A server replay attack may be possible!" 1>&2
76 fi
77 fi
78}
79
fad96766 80download_sig() {
33aa351a
SG
81 if ! download_file $1 $2 noexit; then
82 if [ "$DOWNLOAD_VALIDATE" = "true" ]; then
83 if [ "$3" = "normal" ]; then
84 echo "ERROR: Failed to download http://${DOWNLOAD_SERVER}/$1" 1>&2
85 exit 1
86 else
87 return 1
88 fi
89 else
90 return 0
91 fi
fad96766
DE
92 fi
93}
94
71d3a659
SG
95gpg_setup() {
96 if [ "$DOWNLOAD_VALIDATE" = "false" ]; then
97 return
98 fi
99
b0f0932a
SG
100 if [ "$DOWNLOAD_READY_GPG" = "true" ]; then
101 return
102 fi
103
71d3a659
SG
104 echo "Setting up the GPG keyring"
105
106 mkdir -p "$DOWNLOAD_TEMP/gpg"
107 chmod 700 "$DOWNLOAD_TEMP/gpg"
108 export GNUPGHOME="$DOWNLOAD_TEMP/gpg"
109 if ! gpg --keyserver $DOWNLOAD_KEYSERVER \
110 --recv-keys ${DOWNLOAD_KEYID} >/dev/null 2>&1; then
111 echo "ERROR: Unable to fetch GPG key from keyserver."
112 exit 1
113 fi
b0f0932a
SG
114
115 DOWNLOAD_READY_GPG="true"
71d3a659
SG
116}
117
118gpg_validate() {
119 if [ "$DOWNLOAD_VALIDATE" = "false" ]; then
120 if [ "$DOWNLOAD_SHOW_GPG_WARNING" = "true" ]; then
121 echo "WARNING: Running without gpg validation!" 1>&2
122 fi
123 DOWNLOAD_SHOW_GPG_WARNING="false"
124 return 0
125 fi
126
127 if ! gpg --verify $1 >/dev/zero 2>&1; then
128 echo "ERROR: Invalid signature for $1" 1>&2
129 exit 1
130 fi
131}
132
133in_userns() {
134 [ -e /proc/self/uid_map ] || { echo no; return; }
135 [ "$(wc -l /proc/self/uid_map | awk '{ print $1 }')" -eq 1 ] || \
136 { echo yes; return; }
137 line=$(awk '{ print $1 " " $2 " " $3 }' /proc/self/uid_map)
138 [ "$line" = "0 0 4294967295" ] && { echo no; return; }
139 echo yes
140}
141
142relevant_file() {
143 FILE_PATH="${LXC_CACHE_PATH}/$1"
144 if [ -e "${FILE_PATH}-${DOWNLOAD_MODE}" ]; then
145 FILE_PATH="${FILE_PATH}-${DOWNLOAD_MODE}"
146 fi
147 if [ -e "$FILE_PATH.${DOWNLOAD_COMPAT_LEVEL}" ]; then
148 FILE_PATH="${FILE_PATH}.${DOWNLOAD_COMPAT_LEVEL}"
149 fi
150
151 echo $FILE_PATH
152}
153
154usage() {
155 cat <<EOF
156LXC container image downloader
157
158Required arguments:
159[ -d | --dist <distribution> ]: The name of the distribution
160[ -r | --release <release> ]: Release name/version
161[ -a | --arch <architecture> ]: Architecture of the container
71d3a659
SG
162
163Optional arguments:
10a5fab6
SG
164[ -h | --help ]: This help message
165[ -l | --list ]: List all available images
71d3a659
SG
166[ --variant <variant> ]: Variant of the image (default: "default")
167[ --server <server> ]: Image server (default: "images.linuxcontainers.org")
168[ --keyid <keyid> ]: GPG keyid (default: 0x...)
169[ --keyserver <keyserver> ]: GPG keyserver to use
170[ --no-validate ]: Disable GPG validation (not recommended)
171[ --flush-cache ]: Flush the local copy (if present)
9accc2ef 172[ --force-cache ]; Force the use of the local copy even if expired
71d3a659
SG
173
174LXC internal arguments (do not pass manually!):
175[ --name <name> ]: The container name
176[ --path <path> ]: The path to the container
177[ --rootfs <rootfs> ]: The path to the container's rootfs
178[ --mapped-uid <map> ]: A uid/gid map (user namespaces)
179EOF
180 return 0
181}
182
10a5fab6 183options=$(getopt -o d:r:a:hl -l dist:,release:,arch:,help,list,variant:,\
c1becef2 184server:,keyid:,no-validate,flush-cache,force-cache,name:,path:,\
9accc2ef 185rootfs:,mapped-uid: -- "$@")
71d3a659
SG
186
187if [ $? -ne 0 ]; then
188 usage
189 exit 1
190fi
191eval set -- "$options"
192
193while :; do
194 case "$1" in
10a5fab6
SG
195 -h|--help) usage && exit 1;;
196 -l|--list) DOWNLOAD_LIST_IMAGES="true"; shift 1;;
71d3a659
SG
197 -d|--dist) DOWNLOAD_DIST=$2; shift 2;;
198 -r|--release) DOWNLOAD_RELEASE=$2; shift 2;;
199 -a|--arch) DOWNLOAD_ARCH=$2; shift 2;;
200 --variant) DOWNLOAD_VARIANT=$2; shift 2;;
201 --server) DOWNLOAD_SERVER=$2; shift 2;;
202 --keyid) DOWNLOAD_KEYID=$2; shift 2;;
203 --no-validate) DOWNLOAD_VALIDATE="false"; shift 1;;
204 --flush-cache) DOWNLOAD_FLUSH_CACHE="true"; shift 1;;
9accc2ef 205 --force-cache) DOWNLOAD_FORCE_CACHE="true"; shift 1;;
71d3a659
SG
206 --name) LXC_NAME=$2; shift 2;;
207 --path) LXC_PATH=$2; shift 2;;
208 --rootfs) LXC_ROOTFS=$2; shift 2;;
209 --mapped-uid) LXC_MAPPED_UID=$2; shift 2;;
210 *) break;;
211 esac
212done
213
214# Check for required binaries
215for bin in tar xz wget; do
216 if ! type $bin >/dev/null 2>&1; then
217 echo "ERROR: Missing required tool: $bin" 1>&2
218 exit 1
219 fi
220done
221
222# Check for GPG
223if [ "$DOWNLOAD_VALIDATE" = "true" ]; then
224 if ! type gpg >/dev/null 2>&1; then
225 echo "ERROR: Missing recommended tool: gpg" 1>&2
226 echo "You can workaround this by using --no-validate." 1>&2
227 exit 1
228 fi
229fi
230
231# Check that we have all variables we need
232if [ -z "$LXC_NAME" ] || [ -z "$LXC_PATH" ] || [ -z "$LXC_ROOTFS" ]; then
233 echo "ERROR: Not running through LXC." 1>&2
234 exit 1
235fi
236
237if [ "$(in_userns)" = "yes" ]; then
238 if [ -z "$LXC_MAPPED_UID" ] || [ "$LXC_MAPPED_UID" = "-1" ]; then
239 echo "ERROR: In a user namespace without a map." 1>&2
240 exit 1
241 fi
242 DOWNLOAD_MODE="user"
243fi
244
b0f0932a
SG
245if [ -z "$DOWNLOAD_DIST" ] || [ -z "$DOWNLOAD_RELEASE" ] || \
246 [ -z "$DOWNLOAD_ARCH" ]; then
247 DOWNLOAD_INTERACTIVE="true"
71d3a659
SG
248fi
249
250# Trap all exit signals
251trap cleanup EXIT HUP INT TERM
843a5874
SG
252
253if ! type mktemp >/dev/null 2>&1; then
254 DOWNLOAD_TEMP=/tmp/lxc-download.$$
255 mkdir -p $DOWNLOAD_TEMP
256else
257 DOWNLOAD_TEMP=$(mktemp -d)
258fi
71d3a659 259
10a5fab6 260# Simply list images
b0f0932a
SG
261if [ "$DOWNLOAD_LIST_IMAGES" = "true" ] || \
262 [ "$DOWNLOAD_INTERACTIVE" = "true" ]; then
10a5fab6
SG
263 # Initialize GPG
264 gpg_setup
265
266 # Grab the index
267 DOWNLOAD_INDEX_PATH=/meta/1.0/index-${DOWNLOAD_MODE}
268
269 echo "Downloading the image index"
270 if ! download_file ${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL} \
271 ${DOWNLOAD_TEMP}/index noexit ||
272 ! download_sig ${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL}.asc \
273 ${DOWNLOAD_TEMP}/index.asc noexit; then
274 download_file ${DOWNLOAD_INDEX_PATH} ${DOWNLOAD_TEMP}/index normal
275 download_sig ${DOWNLOAD_INDEX_PATH}.asc \
276 ${DOWNLOAD_TEMP}/index.asc normal
277 fi
278
279 gpg_validate ${DOWNLOAD_TEMP}/index.asc
280
281 # Parse it
282 echo ""
283 echo "---"
284 echo "DIST\tRELEASE\tARCH\tVARIANT\tBUILD"
285 echo "---"
286 while read line; do
287 # Basic CSV parser
288 OLD_IFS=$IFS
289 IFS=";"
290 set -- $line
291 IFS=$OLD_IFS
292
293 [ -n "$DOWNLOAD_DIST" ] && [ "$1" != "$DOWNLOAD_DIST" ] && continue
294 [ -n "$DOWNLOAD_RELEASE" ] && [ "$2" != "$DOWNLOAD_RELEASE" ] && continue
295 [ -n "$DOWNLOAD_ARCH" ] && [ "$3" != "$DOWNLOAD_ARCH" ] && continue
296 [ -n "$DOWNLOAD_VARIANT" ] && [ "$4" != "$DOWNLOAD_VARIANT" ] && continue
297 [ -z "$5" ] || [ -z "$6" ] && continue
298
299 echo "$1\t$2\t$3\t$4\t$5"
300 done < ${DOWNLOAD_TEMP}/index
301 echo "---"
302
b0f0932a
SG
303 if [ "$DOWNLOAD_LIST_IMAGES" = "true" ]; then
304 exit 1
305 fi
306
307 # Interactive mode
308 echo ""
309
310 if [ -z "$DOWNLOAD_DIST" ]; then
311 echo -n "Distribution: "
312 read DOWNLOAD_DIST
313 fi
314
315 if [ -z "$DOWNLOAD_RELEASE" ]; then
316 echo -n "Release: "
317 read DOWNLOAD_RELEASE
318 fi
319
320 if [ -z "$DOWNLOAD_ARCH" ]; then
321 echo -n "Architecture: "
322 read DOWNLOAD_ARCH
323 fi
324
325 echo ""
10a5fab6
SG
326fi
327
71d3a659
SG
328# Setup the cache
329if [ "$DOWNLOAD_MODE" = "system" ]; then
b56661fe 330 LXC_CACHE_BASE="$LOCALSTATEDIR/cache/lxc/"
71d3a659
SG
331else
332 LXC_CACHE_BASE="$HOME/.cache/lxc/"
71d3a659
SG
333fi
334
b56661fe
SG
335LXC_CACHE_PATH="$LXC_CACHE_BASE/download/$DOWNLOAD_DIST"
336LXC_CACHE_PATH="$LXC_CACHE_PATH/$DOWNLOAD_RELEASE/$DOWNLOAD_ARCH/"
337LXC_CACHE_PATH="$LXC_CACHE_PATH/$DOWNLOAD_VARIANT"
338
71d3a659
SG
339if [ -d "$LXC_CACHE_PATH" ]; then
340 if [ "$DOWNLOAD_FLUSH_CACHE" = "true" ]; then
341 echo "Flushing the cache..."
342 rm -Rf $LXC_CACHE_PATH
9accc2ef
SG
343 elif [ "$DOWNLOAD_FORCE_CACHE" = "true" ]; then
344 DOWNLOAD_USE_CACHE="true"
71d3a659
SG
345 else
346 DOWNLOAD_USE_CACHE="true"
347 if [ -e "$(relevant_file expiry)" ]; then
348 if [ "$(cat $(relevant_file expiry))" -lt $(date +%s) ]; then
349 echo "The cached copy has expired, re-downloading..."
350 DOWNLOAD_USE_CACHE="false"
71d3a659
SG
351 fi
352 fi
353 fi
354fi
355
356# Download what's needed
357if [ "$DOWNLOAD_USE_CACHE" = "false" ]; then
358 # Initialize GPG
359 gpg_setup
360
361 # Grab the index
362 DOWNLOAD_INDEX_PATH=/meta/1.0/index-${DOWNLOAD_MODE}
363
364 echo "Downloading the image index"
365 if ! download_file ${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL} \
366 ${DOWNLOAD_TEMP}/index noexit ||
33aa351a 367 ! download_sig ${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL}.asc \
71d3a659
SG
368 ${DOWNLOAD_TEMP}/index.asc noexit; then
369 download_file ${DOWNLOAD_INDEX_PATH} ${DOWNLOAD_TEMP}/index normal
fad96766 370 download_sig ${DOWNLOAD_INDEX_PATH}.asc \
33aa351a 371 ${DOWNLOAD_TEMP}/index.asc normal
71d3a659
SG
372 fi
373
374 gpg_validate ${DOWNLOAD_TEMP}/index.asc
375
376 # Parse it
377 while read line; do
378 # Basic CSV parser
379 OLD_IFS=$IFS
380 IFS=";"
381 set -- $line
382 IFS=$OLD_IFS
383
384 if [ "$1" != "$DOWNLOAD_DIST" ] || \
385 [ "$2" != "$DOWNLOAD_RELEASE" ] || \
386 [ "$3" != "$DOWNLOAD_ARCH" ] || \
387 [ "$4" != "$DOWNLOAD_VARIANT" ] || \
388 [ -z "$6" ]; then
389 continue
390 fi
391
9accc2ef 392 DOWNLOAD_BUILD=$5
71d3a659
SG
393 DOWNLOAD_URL=$6
394 break
395 done < ${DOWNLOAD_TEMP}/index
396
397 if [ -z "$DOWNLOAD_URL" ]; then
398 echo "ERROR: Couldn't find a matching image." 1>&1
399 exit 1
400 fi
401
9accc2ef
SG
402 if [ -d "$LXC_CACHE_PATH" ] && [ -f "$LXC_CACHE_PATH/build_id" ] && \
403 [ "$(cat $LXC_CACHE_PATH/build_id)" = "$DOWNLOAD_BUILD" ]; then
404 echo "The cache is already up to date."
405 echo "Using image from local cache"
406 else
407 # Download the actual files
408 echo "Downloading the rootfs"
409 download_file $DOWNLOAD_URL/rootfs.tar.xz \
410 ${DOWNLOAD_TEMP}/rootfs.tar.xz normal
411 download_sig $DOWNLOAD_URL/rootfs.tar.xz.asc \
412 ${DOWNLOAD_TEMP}/rootfs.tar.xz.asc normal
413 gpg_validate ${DOWNLOAD_TEMP}/rootfs.tar.xz.asc
414
415 echo "Downloading the metadata"
416 download_file $DOWNLOAD_URL/meta.tar.xz \
417 ${DOWNLOAD_TEMP}/meta.tar.xz normal
418 download_sig $DOWNLOAD_URL/meta.tar.xz.asc \
419 ${DOWNLOAD_TEMP}/meta.tar.xz.asc normal
420 gpg_validate ${DOWNLOAD_TEMP}/meta.tar.xz.asc
421
422 if [ -d $LXC_CACHE_PATH ]; then
423 rm -Rf $LXC_CACHE_PATH
424 fi
425 mkdir -p $LXC_CACHE_PATH
426 mv ${DOWNLOAD_TEMP}/rootfs.tar.xz $LXC_CACHE_PATH
427 if ! tar Jxf ${DOWNLOAD_TEMP}/meta.tar.xz -C $LXC_CACHE_PATH; then
428 echo "ERROR: Invalid rootfs tarball." 2>&1
429 exit 1
430 fi
71d3a659 431
9accc2ef
SG
432 echo $DOWNLOAD_BUILD > $LXC_CACHE_PATH/build_id
433
434 if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then
0d656b05 435 chown -R $LXC_MAPPED_UID $LXC_CACHE_BASE >/dev/null 2>&1 || true
9accc2ef
SG
436 fi
437 echo "The image cache is now ready"
71d3a659 438 fi
71d3a659
SG
439else
440 echo "Using image from local cache"
441fi
442
443# Unpack the rootfs
444echo "Unpacking the rootfs"
fecf101c
SG
445
446EXCLUDES=""
447excludelist=$(relevant_file excludes)
448if [ -f "${excludelist}" ]; then
449 while read line; do
450 EXCLUDES="$EXCLUDES --exclude=$line"
451 done < $excludelist
71d3a659
SG
452fi
453
fecf101c
SG
454tar --anchored ${EXCLUDES} --numeric-owner -xpJf \
455 ${LXC_CACHE_PATH}/rootfs.tar.xz -C ${LXC_ROOTFS}
456
457mkdir -p ${LXC_ROOTFS}/dev/pts/
458
71d3a659
SG
459# Setup the configuration
460configfile=$(relevant_file config)
461fstab=$(relevant_file fstab)
462if [ ! -e $configfile ]; then
463 echo "ERROR: meta tarball is missing the configuration file" 1>&2
464 exit 1
465fi
466
467## Extract all the network config entries
468sed -i -e "/lxc.network/{w ${LXC_PATH}/config-network" -e "d}" \
469 ${LXC_PATH}/config
470
471## Extract any other config entry
472sed -i -e "/lxc./{w ${LXC_PATH}/config-auto" -e "d}" ${LXC_PATH}/config
473
474## Append the defaults
475echo "" >> ${LXC_PATH}/config
476echo "# Distribution configuration" >> ${LXC_PATH}/config
477cat $configfile >> ${LXC_PATH}/config
478
479## Add the container-specific config
480echo "" >> ${LXC_PATH}/config
481echo "# Container specific configuration" >> ${LXC_PATH}/config
482if [ -e "${LXC_PATH}/config-auto" ]; then
483 cat ${LXC_PATH}/config-auto >> ${LXC_PATH}/config
484 rm ${LXC_PATH}/config-auto
485fi
486if [ -e "$fstab" ]; then
487 echo "lxc.mount = ${LXC_PATH}/fstab" >> ${LXC_PATH}/config
488fi
489echo "lxc.utsname = ${LXC_NAME}" >> ${LXC_PATH}/config
490
491## Re-add the previously removed network config
492if [ -e "${LXC_PATH}/config-network" ]; then
493 echo "" >> ${LXC_PATH}/config
494 echo "# Network configuration" >> ${LXC_PATH}/config
495 cat ${LXC_PATH}/config-network >> ${LXC_PATH}/config
496 rm ${LXC_PATH}/config-network
497fi
498
499TEMPLATE_FILES="${LXC_PATH}/config"
500
501# Setup the fstab
502if [ -e $fstab ]; then
503 cp ${fstab} ${LXC_PATH}/fstab
504 TEMPLATE_FILES="$TEMPLATE_FILES ${LXC_PATH}/fstab"
505fi
506
507# Look for extra templates
508if [ -e "$(relevant_file templates)" ]; then
509 while read line; do
510 fullpath=${LXC_ROOTFS}/$line
511 [ ! -e "$fullpath" ] && continue
512 TEMPLATE_FILES="$TEMPLATE_FILES $fullpath"
513 done < $(relevant_file templates)
514fi
515
516# Replace variables in all templates
517for file in $TEMPLATE_FILES; do
fad96766 518 [ ! -f "$file" ] && continue
71d3a659
SG
519
520 sed -i "s#LXC_NAME#$LXC_NAME#g" $file
521 sed -i "s#LXC_PATH#$LXC_PATH#g" $file
522 sed -i "s#LXC_ROOTFS#$LXC_ROOTFS#g" $file
523 sed -i "s#LXC_TEMPLATE_CONFIG#$LXC_TEMPLATE_CONFIG#g" $file
524 sed -i "s#LXC_HOOK_DIR#$LXC_HOOK_DIR#g" $file
525done
526
527if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then
0d656b05 528 chown $LXC_MAPPED_UID $LXC_PATH/config $LXC_PATH/fstab >/dev/null 2>&1 || true
71d3a659
SG
529fi
530
531if [ -e "$(relevant_file create-message)" ]; then
532 echo ""
533 echo "---"
534 cat "$(relevant_file create-message)"
535fi
536
537exit 0