]> git.proxmox.com Git - mirror_lxc.git/blame - templates/lxc-download.in
download: Fix download_sig logic
[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"
38DOWNLOAD_MODE="system"
39DOWNLOAD_USE_CACHE="false"
40DOWNLOAD_URL=
41DOWNLOAD_SHOW_HTTP_WARNING="true"
42DOWNLOAD_SHOW_GPG_WARNING="true"
43DOWNLOAD_COMPAT_LEVEL=1
44
45LXC_NAME=
46LXC_PATH=
47LXC_ROOTFS=
48LXC_MAPPED_UID=
49
50# Some useful functions
51cleanup() {
52 if [ -d "$DOWNLOAD_TEMP" ]; then
53 rm -Rf $DOWNLOAD_TEMP
54 fi
55}
56
57download_file() {
58 if ! wget -q https://${DOWNLOAD_SERVER}/$1 -O $2 >/dev/null 2>&1; then
59 if ! wget -q http://${DOWNLOAD_SERVER}/$1 -O $2 >/dev/null 2>&1; then
60 if [ "$3" = "noexit" ]; then
61 return 1
62 else
fad96766 63 echo "ERROR: Failed to download http://${DOWNLOAD_SERVER}/$1" 1>&2
71d3a659
SG
64 exit 1
65 fi
66 elif [ "$DOWNLOAD_SHOW_HTTP_WARNING" = "true" ]; then
67 DOWNLOAD_SHOW_HTTP_WARNING="false"
68 echo "WARNING: Failed to download the file over HTTPs." 1>&2
69 echo -n " The file was instead download over HTTP. " 1>&2
70 echo "A server replay attack may be possible!" 1>&2
71 fi
72 fi
73}
74
fad96766 75download_sig() {
33aa351a
SG
76 if ! download_file $1 $2 noexit; then
77 if [ "$DOWNLOAD_VALIDATE" = "true" ]; then
78 if [ "$3" = "normal" ]; then
79 echo "ERROR: Failed to download http://${DOWNLOAD_SERVER}/$1" 1>&2
80 exit 1
81 else
82 return 1
83 fi
84 else
85 return 0
86 fi
fad96766
DE
87 fi
88}
89
71d3a659
SG
90gpg_setup() {
91 if [ "$DOWNLOAD_VALIDATE" = "false" ]; then
92 return
93 fi
94
95 echo "Setting up the GPG keyring"
96
97 mkdir -p "$DOWNLOAD_TEMP/gpg"
98 chmod 700 "$DOWNLOAD_TEMP/gpg"
99 export GNUPGHOME="$DOWNLOAD_TEMP/gpg"
100 if ! gpg --keyserver $DOWNLOAD_KEYSERVER \
101 --recv-keys ${DOWNLOAD_KEYID} >/dev/null 2>&1; then
102 echo "ERROR: Unable to fetch GPG key from keyserver."
103 exit 1
104 fi
105}
106
107gpg_validate() {
108 if [ "$DOWNLOAD_VALIDATE" = "false" ]; then
109 if [ "$DOWNLOAD_SHOW_GPG_WARNING" = "true" ]; then
110 echo "WARNING: Running without gpg validation!" 1>&2
111 fi
112 DOWNLOAD_SHOW_GPG_WARNING="false"
113 return 0
114 fi
115
116 if ! gpg --verify $1 >/dev/zero 2>&1; then
117 echo "ERROR: Invalid signature for $1" 1>&2
118 exit 1
119 fi
120}
121
122in_userns() {
123 [ -e /proc/self/uid_map ] || { echo no; return; }
124 [ "$(wc -l /proc/self/uid_map | awk '{ print $1 }')" -eq 1 ] || \
125 { echo yes; return; }
126 line=$(awk '{ print $1 " " $2 " " $3 }' /proc/self/uid_map)
127 [ "$line" = "0 0 4294967295" ] && { echo no; return; }
128 echo yes
129}
130
131relevant_file() {
132 FILE_PATH="${LXC_CACHE_PATH}/$1"
133 if [ -e "${FILE_PATH}-${DOWNLOAD_MODE}" ]; then
134 FILE_PATH="${FILE_PATH}-${DOWNLOAD_MODE}"
135 fi
136 if [ -e "$FILE_PATH.${DOWNLOAD_COMPAT_LEVEL}" ]; then
137 FILE_PATH="${FILE_PATH}.${DOWNLOAD_COMPAT_LEVEL}"
138 fi
139
140 echo $FILE_PATH
141}
142
143usage() {
144 cat <<EOF
145LXC container image downloader
146
147Required arguments:
148[ -d | --dist <distribution> ]: The name of the distribution
149[ -r | --release <release> ]: Release name/version
150[ -a | --arch <architecture> ]: Architecture of the container
151[ -h | --help ]: This help message
152
153Optional arguments:
154[ --variant <variant> ]: Variant of the image (default: "default")
155[ --server <server> ]: Image server (default: "images.linuxcontainers.org")
156[ --keyid <keyid> ]: GPG keyid (default: 0x...)
157[ --keyserver <keyserver> ]: GPG keyserver to use
158[ --no-validate ]: Disable GPG validation (not recommended)
159[ --flush-cache ]: Flush the local copy (if present)
160
161LXC internal arguments (do not pass manually!):
162[ --name <name> ]: The container name
163[ --path <path> ]: The path to the container
164[ --rootfs <rootfs> ]: The path to the container's rootfs
165[ --mapped-uid <map> ]: A uid/gid map (user namespaces)
166EOF
167 return 0
168}
169
170options=$(getopt -o d:r:a:h -l dist:,release:,arch:,help,variant:,server:,\
171keyid:,no-validate,flush-cache,name:,path:,rootfs:,mapped-uid: -- "$@")
172
173if [ $? -ne 0 ]; then
174 usage
175 exit 1
176fi
177eval set -- "$options"
178
179while :; do
180 case "$1" in
181 -h|--help) usage $0 && exit 0;;
182 -d|--dist) DOWNLOAD_DIST=$2; shift 2;;
183 -r|--release) DOWNLOAD_RELEASE=$2; shift 2;;
184 -a|--arch) DOWNLOAD_ARCH=$2; shift 2;;
185 --variant) DOWNLOAD_VARIANT=$2; shift 2;;
186 --server) DOWNLOAD_SERVER=$2; shift 2;;
187 --keyid) DOWNLOAD_KEYID=$2; shift 2;;
188 --no-validate) DOWNLOAD_VALIDATE="false"; shift 1;;
189 --flush-cache) DOWNLOAD_FLUSH_CACHE="true"; shift 1;;
190 --name) LXC_NAME=$2; shift 2;;
191 --path) LXC_PATH=$2; shift 2;;
192 --rootfs) LXC_ROOTFS=$2; shift 2;;
193 --mapped-uid) LXC_MAPPED_UID=$2; shift 2;;
194 *) break;;
195 esac
196done
197
198# Check for required binaries
199for bin in tar xz wget; do
200 if ! type $bin >/dev/null 2>&1; then
201 echo "ERROR: Missing required tool: $bin" 1>&2
202 exit 1
203 fi
204done
205
206# Check for GPG
207if [ "$DOWNLOAD_VALIDATE" = "true" ]; then
208 if ! type gpg >/dev/null 2>&1; then
209 echo "ERROR: Missing recommended tool: gpg" 1>&2
210 echo "You can workaround this by using --no-validate." 1>&2
211 exit 1
212 fi
213fi
214
215# Check that we have all variables we need
216if [ -z "$LXC_NAME" ] || [ -z "$LXC_PATH" ] || [ -z "$LXC_ROOTFS" ]; then
217 echo "ERROR: Not running through LXC." 1>&2
218 exit 1
219fi
220
221if [ "$(in_userns)" = "yes" ]; then
222 if [ -z "$LXC_MAPPED_UID" ] || [ "$LXC_MAPPED_UID" = "-1" ]; then
223 echo "ERROR: In a user namespace without a map." 1>&2
224 exit 1
225 fi
226 DOWNLOAD_MODE="user"
227fi
228
229if [ -z "$DOWNLOAD_DIST" ] || [ -z "$DOWNLOAD_RELEASE" ] || \
230 [ -z "$DOWNLOAD_ARCH" ]; then
231 echo "ERROR: Missing required argument" 1>&2
232 usage
233 exit 1
234fi
235
236# Trap all exit signals
237trap cleanup EXIT HUP INT TERM
238DOWNLOAD_TEMP=$(mktemp -d)
239
240# Setup the cache
241if [ "$DOWNLOAD_MODE" = "system" ]; then
242 LXC_CACHE_BASE="$LOCALSTATEDIR/cache/"
243 LXC_CACHE_PATH="$LOCALSTATEDIR/cache/lxc/download/$DOWNLOAD_DIST"
244 LXC_CACHE_PATH="$LXC_CACHE_PATH/$DOWNLOAD_RELEASE/$DOWNLOAD_ARCH"
245else
246 LXC_CACHE_BASE="$HOME/.cache/lxc/"
247 LXC_CACHE_PATH="$HOME/.cache/lxc/download/$DOWNLOAD_DIST"
248 LXC_CACHE_PATH="$LXC_CACHE_PATH/$DOWNLOAD_RELEASE/$DOWNLOAD_ARCH"
249fi
250
251if [ -d "$LXC_CACHE_PATH" ]; then
252 if [ "$DOWNLOAD_FLUSH_CACHE" = "true" ]; then
253 echo "Flushing the cache..."
254 rm -Rf $LXC_CACHE_PATH
255 else
256 DOWNLOAD_USE_CACHE="true"
257 if [ -e "$(relevant_file expiry)" ]; then
258 if [ "$(cat $(relevant_file expiry))" -lt $(date +%s) ]; then
259 echo "The cached copy has expired, re-downloading..."
260 DOWNLOAD_USE_CACHE="false"
261 rm -Rf $LXC_CACHE_PATH
262 fi
263 fi
264 fi
265fi
266
267# Download what's needed
268if [ "$DOWNLOAD_USE_CACHE" = "false" ]; then
269 # Initialize GPG
270 gpg_setup
271
272 # Grab the index
273 DOWNLOAD_INDEX_PATH=/meta/1.0/index-${DOWNLOAD_MODE}
274
275 echo "Downloading the image index"
276 if ! download_file ${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL} \
277 ${DOWNLOAD_TEMP}/index noexit ||
33aa351a 278 ! download_sig ${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL}.asc \
71d3a659
SG
279 ${DOWNLOAD_TEMP}/index.asc noexit; then
280 download_file ${DOWNLOAD_INDEX_PATH} ${DOWNLOAD_TEMP}/index normal
fad96766 281 download_sig ${DOWNLOAD_INDEX_PATH}.asc \
33aa351a 282 ${DOWNLOAD_TEMP}/index.asc normal
71d3a659
SG
283 fi
284
285 gpg_validate ${DOWNLOAD_TEMP}/index.asc
286
287 # Parse it
288 while read line; do
289 # Basic CSV parser
290 OLD_IFS=$IFS
291 IFS=";"
292 set -- $line
293 IFS=$OLD_IFS
294
295 if [ "$1" != "$DOWNLOAD_DIST" ] || \
296 [ "$2" != "$DOWNLOAD_RELEASE" ] || \
297 [ "$3" != "$DOWNLOAD_ARCH" ] || \
298 [ "$4" != "$DOWNLOAD_VARIANT" ] || \
299 [ -z "$6" ]; then
300 continue
301 fi
302
303 DOWNLOAD_URL=$6
304 break
305 done < ${DOWNLOAD_TEMP}/index
306
307 if [ -z "$DOWNLOAD_URL" ]; then
308 echo "ERROR: Couldn't find a matching image." 1>&1
309 exit 1
310 fi
311
312 # Download the actual files
313 echo "Downloading the rootfs"
314 download_file $DOWNLOAD_URL/rootfs.tar.xz \
315 ${DOWNLOAD_TEMP}/rootfs.tar.xz normal
fad96766 316 download_sig $DOWNLOAD_URL/rootfs.tar.xz.asc \
71d3a659
SG
317 ${DOWNLOAD_TEMP}/rootfs.tar.xz.asc normal
318 gpg_validate ${DOWNLOAD_TEMP}/rootfs.tar.xz.asc
319
320 echo "Downloading the metadata"
321 download_file $DOWNLOAD_URL/meta.tar.xz \
322 ${DOWNLOAD_TEMP}/meta.tar.xz normal
fad96766 323 download_sig $DOWNLOAD_URL/meta.tar.xz.asc \
71d3a659
SG
324 ${DOWNLOAD_TEMP}/meta.tar.xz.asc normal
325 gpg_validate ${DOWNLOAD_TEMP}/meta.tar.xz.asc
326
327 mkdir -p $LXC_CACHE_PATH
328 mv ${DOWNLOAD_TEMP}/rootfs.tar.xz $LXC_CACHE_PATH
329 if ! tar Jxf ${DOWNLOAD_TEMP}/meta.tar.xz -C $LXC_CACHE_PATH; then
330 echo "ERROR: Invalid rootfs tarball." 2>&1
331 exit 1
332 fi
333
334 if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then
335 chown $LXC_MAPPED_UID -Rf $LXC_CACHE_BASE >/dev/null 2>&1 || true
336 fi
337 echo "The image cache is now ready"
338else
339 echo "Using image from local cache"
340fi
341
342# Unpack the rootfs
343echo "Unpacking the rootfs"
344if [ "$DOWNLOAD_MODE" = "system" ]; then
345 tar --numeric-owner -xpJf ${LXC_CACHE_PATH}/rootfs.tar.xz -C ${LXC_ROOTFS}
346else
347 tar --anchored --exclude="./dev/*" --numeric-owner -xpJf \
348 ${LXC_CACHE_PATH}/rootfs.tar.xz -C ${LXC_ROOTFS}
349 mkdir -p ${LXC_ROOTFS}/dev/pts/
350fi
351
352# Setup the configuration
353configfile=$(relevant_file config)
354fstab=$(relevant_file fstab)
355if [ ! -e $configfile ]; then
356 echo "ERROR: meta tarball is missing the configuration file" 1>&2
357 exit 1
358fi
359
360## Extract all the network config entries
361sed -i -e "/lxc.network/{w ${LXC_PATH}/config-network" -e "d}" \
362 ${LXC_PATH}/config
363
364## Extract any other config entry
365sed -i -e "/lxc./{w ${LXC_PATH}/config-auto" -e "d}" ${LXC_PATH}/config
366
367## Append the defaults
368echo "" >> ${LXC_PATH}/config
369echo "# Distribution configuration" >> ${LXC_PATH}/config
370cat $configfile >> ${LXC_PATH}/config
371
372## Add the container-specific config
373echo "" >> ${LXC_PATH}/config
374echo "# Container specific configuration" >> ${LXC_PATH}/config
375if [ -e "${LXC_PATH}/config-auto" ]; then
376 cat ${LXC_PATH}/config-auto >> ${LXC_PATH}/config
377 rm ${LXC_PATH}/config-auto
378fi
379if [ -e "$fstab" ]; then
380 echo "lxc.mount = ${LXC_PATH}/fstab" >> ${LXC_PATH}/config
381fi
382echo "lxc.utsname = ${LXC_NAME}" >> ${LXC_PATH}/config
383
384## Re-add the previously removed network config
385if [ -e "${LXC_PATH}/config-network" ]; then
386 echo "" >> ${LXC_PATH}/config
387 echo "# Network configuration" >> ${LXC_PATH}/config
388 cat ${LXC_PATH}/config-network >> ${LXC_PATH}/config
389 rm ${LXC_PATH}/config-network
390fi
391
392TEMPLATE_FILES="${LXC_PATH}/config"
393
394# Setup the fstab
395if [ -e $fstab ]; then
396 cp ${fstab} ${LXC_PATH}/fstab
397 TEMPLATE_FILES="$TEMPLATE_FILES ${LXC_PATH}/fstab"
398fi
399
400# Look for extra templates
401if [ -e "$(relevant_file templates)" ]; then
402 while read line; do
403 fullpath=${LXC_ROOTFS}/$line
404 [ ! -e "$fullpath" ] && continue
405 TEMPLATE_FILES="$TEMPLATE_FILES $fullpath"
406 done < $(relevant_file templates)
407fi
408
409# Replace variables in all templates
410for file in $TEMPLATE_FILES; do
fad96766 411 [ ! -f "$file" ] && continue
71d3a659
SG
412
413 sed -i "s#LXC_NAME#$LXC_NAME#g" $file
414 sed -i "s#LXC_PATH#$LXC_PATH#g" $file
415 sed -i "s#LXC_ROOTFS#$LXC_ROOTFS#g" $file
416 sed -i "s#LXC_TEMPLATE_CONFIG#$LXC_TEMPLATE_CONFIG#g" $file
417 sed -i "s#LXC_HOOK_DIR#$LXC_HOOK_DIR#g" $file
418done
419
420if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then
421 chown $LXC_MAPPED_UID -f $LXC_PATH/config $LXC_PATH/fstab || true
422fi
423
424if [ -e "$(relevant_file create-message)" ]; then
425 echo ""
426 echo "---"
427 cat "$(relevant_file create-message)"
428fi
429
430exit 0