]> git.proxmox.com Git - mirror_lxc.git/blame - templates/lxc-oci.in
caps: fix illegal access to array bound
[mirror_lxc.git] / templates / lxc-oci.in
CommitLineData
0ef43a5c
SH
1#!/bin/bash
2
3# Create application containers from OCI images
4
5# Copyright © 2014 Stéphane Graber <stgraber@ubuntu.com>
6# Copyright © 2017 Serge Hallyn <serge@hallyn.com>
7#
8# This library is free software; you can redistribute it and/or
9# modify it under the terms of the GNU Lesser General Public
10# License as published by the Free Software Foundation; either
11# version 2.1 of the License, or (at your option) any later version.
12
13# This library is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16# Lesser General Public License for more details.
17
18# You should have received a copy of the GNU Lesser General Public
19# License along with this library; if not, write to the Free Software
20# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21# USA
22
23set -eu
0ef43a5c
SH
24
25# Make sure the usual locations are in PATH
26export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin
27
28# Check for required binaries
29for bin in skopeo umoci jq; do
4328e9e3
CB
30 if ! which $bin >/dev/null 2>&1; then
31 echo "ERROR: Missing required tool: $bin" 1>&2
32 exit 1
33 fi
0ef43a5c
SH
34done
35
52e31c07 36LOCALSTATEDIR="@LOCALSTATEDIR@"
d7c685c6 37LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@"
9a962dc6 38LXC_HOOK_DIR="@LXCHOOKDIR@"
d7c685c6 39
0ef43a5c
SH
40# Some useful functions
41cleanup() {
4328e9e3
CB
42 if [ -d "${DOWNLOAD_TEMP}" ]; then
43 rm -Rf "${DOWNLOAD_TEMP}"
44 fi
45
46 if [ -d "${LXC_ROOTFS}.tmp" ]; then
47 rm -Rf "${LXC_ROOTFS}.tmp"
48 fi
0ef43a5c
SH
49}
50
51in_userns() {
4328e9e3
CB
52 [ -e /proc/self/uid_map ] || { echo no; return; }
53 while read -r line; do
54 fields="$(echo "$line" | awk '{ print $1 " " $2 " " $3 }')"
55 if [ "${fields}" = "0 0 4294967295" ]; then
56 echo no;
57 return;
58 fi
59 if echo "${fields}" | grep -q " 0 1$"; then
60 echo userns-root;
61 return;
62 fi
63 done < /proc/self/uid_map
64
65 [ "$(cat /proc/self/uid_map)" = "$(cat /proc/1/uid_map)" ] && { echo userns-root; return; }
66 echo yes
0ef43a5c
SH
67}
68
ce59e4ca 69getconfigpath() {
4328e9e3
CB
70 basedir="$1"
71 q="$2"
72
73 digest=$(jq -c -r --arg q "$q" '.manifests[] | if .annotations."org.opencontainers.image.ref.name" == $q then .digest else empty end' < "${basedir}/index.json")
74 if [ -z "${digest}" ]; then
75 echo "$q not found in index.json" >&2
76 return
77 fi
78
79 # Ok we have the image config digest, now get the config from that
80 # shellcheck disable=SC2039
81 d=${digest:7}
82 cdigest=$(jq -c -r '.config.digest' < "${basedir}/blobs/sha256/${d}")
83 if [ -z "${cdigest}" ]; then
84 echo "container config not found" >&2
85 return
86 fi
87
88 # shellcheck disable=SC2039
89 d2=${cdigest:7}
90 echo "${basedir}/blobs/sha256/${d2}"
91 return
ce59e4ca
FA
92}
93
4328e9e3 94# Get entrypoint from oci image. Use sh if unspecified
ce59e4ca 95getep() {
4328e9e3
CB
96 if [ "$#" -eq 0 ]; then
97 echo "/bin/sh"
98 return
99 fi
100
101 configpath="$1"
102
d99e3b2e
FA
103 ep=$(jq -c '.config.Entrypoint[]?'< "${configpath}" | tr '\n' ' ')
104 cmd=$(jq -c '.config.Cmd[]?'< "${configpath}" | tr '\n' ' ')
4328e9e3
CB
105 if [ -z "${ep}" ]; then
106 ep="${cmd}"
107 if [ -z "${ep}" ]; then
108 ep="/bin/sh"
109 fi
110 elif [ -n "${cmd}" ]; then
111 ep="${ep} ${cmd}"
112 fi
113
114 echo "${ep}"
115 return
0ef43a5c
SH
116}
117
996202e7
FA
118# get environment from oci image.
119getenv() {
4328e9e3
CB
120 if [ "$#" -eq 0 ]; then
121 return
122 fi
996202e7 123
4328e9e3 124 configpath="$1"
996202e7 125
4328e9e3 126 env=$(jq -c -r '.config.Env[]'< "${configpath}")
996202e7 127
4328e9e3
CB
128 echo "${env}"
129 return
996202e7
FA
130}
131
e86dcc91
FA
132# FIXME 1: only support numerical values in the configuration file.
133# FIXME 2: from the OCI image spec: "If group/gid is not specified,
134# the default group and supplementary groups of the given user/uid in
135# /etc/passwd from the container are applied."
136getuidgid() {
4328e9e3
CB
137 if [ "$#" -eq 0 ]; then
138 echo "0 0"
139 return
140 fi
e86dcc91 141
4328e9e3 142 configpath="$1"
e86dcc91 143
4328e9e3
CB
144 uidgid=$(jq -c -r '.config.User // "0:0"' < "${configpath}")
145 # shellcheck disable=SC2039
146 uidgid=(${uidgid//:/ })
e86dcc91 147
4328e9e3
CB
148 printf '%d %d' "${uidgid[0]:-0}" "${uidgid[1]:-0}" 2>/dev/null || true
149 return
e86dcc91
FA
150}
151
a787c332
FA
152# get cwd from oci image.
153getcwd() {
4328e9e3
CB
154 if [ "$#" -eq 0 ]; then
155 echo "/"
156 return
157 fi
a787c332 158
4328e9e3 159 configpath="$1"
a787c332 160
4328e9e3 161 cwd=$(jq -c -r '.config.WorkingDir // "/"' < "${configpath}")
a787c332 162
4328e9e3
CB
163 echo "${cwd}"
164 return
a787c332
FA
165}
166
0ef43a5c 167usage() {
4328e9e3 168 cat <<EOF
0ef43a5c
SH
169LXC container template for OCI images
170
171Special arguments:
172[ -h | --help ]: Print this help message and exit.
173
174Required arguments:
175[ -u | --url <url> ]: The OCI image URL
176
797f99c6
FA
177Optional arguments:
178[ --username <username> ]: The username for the registry
179[ --password <password> ]: The password for the registry
180
0ef43a5c
SH
181LXC internal arguments (do not pass manually!):
182[ --name <name> ]: The container name
183[ --path <path> ]: The path to the container
184[ --rootfs <rootfs> ]: The path to the container's rootfs
185[ --mapped-uid <map> ]: A uid map (user namespaces)
186[ --mapped-gid <map> ]: A gid map (user namespaces)
0ef43a5c 187EOF
4328e9e3 188 return 0
0ef43a5c
SH
189}
190
4328e9e3 191if ! options=$(getopt -o u:h -l help,url:,username:,password:,no-cache,dhcp,name:,path:,rootfs:,mapped-uid:,mapped-gid: -- "$@"); then
0ef43a5c
SH
192 usage
193 exit 1
194fi
195eval set -- "$options"
196
197OCI_URL=""
797f99c6
FA
198OCI_USERNAME=
199OCI_PASSWORD=
52e31c07 200OCI_USE_CACHE="true"
9a962dc6 201OCI_USE_DHCP="false"
797f99c6 202
0ef43a5c
SH
203LXC_MAPPED_GID=
204LXC_MAPPED_UID=
205LXC_NAME=
206LXC_PATH=
207LXC_ROOTFS=
208
209while :; do
4328e9e3
CB
210 case "$1" in
211 -h|--help) usage && exit 1;;
212 -u|--url) OCI_URL=$2; shift 2;;
213 --username) OCI_USERNAME=$2; shift 2;;
214 --password) OCI_PASSWORD=$2; shift 2;;
215 --no-cache) OCI_USE_CACHE="false"; shift 1;;
216 --dhcp) OCI_USE_DHCP="true"; shift 1;;
217 --name) LXC_NAME=$2; shift 2;;
218 --path) LXC_PATH=$2; shift 2;;
219 --rootfs) LXC_ROOTFS=$2; shift 2;;
220 --mapped-uid) LXC_MAPPED_UID=$2; shift 2;;
221 --mapped-gid) LXC_MAPPED_GID=$2; shift 2;;
222 *) break;;
223 esac
0ef43a5c
SH
224done
225
226# Check that we have all variables we need
227if [ -z "$LXC_NAME" ] || [ -z "$LXC_PATH" ] || [ -z "$LXC_ROOTFS" ]; then
4328e9e3
CB
228 echo "ERROR: Not running through LXC" 1>&2
229 exit 1
0ef43a5c
SH
230fi
231
232if [ -z "$OCI_URL" ]; then
4328e9e3
CB
233 echo "ERROR: no OCI URL given"
234 exit 1
0ef43a5c
SH
235fi
236
797f99c6 237if [ -n "$OCI_PASSWORD" ] && [ -z "$OCI_USERNAME" ]; then
4328e9e3
CB
238 echo "ERROR: password given but no username specified"
239 exit 1
797f99c6
FA
240fi
241
52e31c07 242if [ "${OCI_USE_CACHE}" = "true" ]; then
4328e9e3
CB
243 if ! skopeo copy --help | grep -q 'dest-shared-blob-dir'; then
244 echo "INFO: skopeo doesn't support blob caching"
245 OCI_USE_CACHE="false"
246 fi
52e31c07
FA
247fi
248
0ef43a5c
SH
249USERNS=$(in_userns)
250
52e31c07 251if [ "$USERNS" = "yes" ]; then
4328e9e3
CB
252 if [ -z "$LXC_MAPPED_UID" ] || [ "$LXC_MAPPED_UID" = "-1" ]; then
253 echo "ERROR: In a user namespace without a map" 1>&2
254 exit 1
255 fi
52e31c07
FA
256fi
257
258if [ "${OCI_USE_CACHE}" = "true" ]; then
4328e9e3
CB
259 if [ "$USERNS" = "yes" ]; then
260 DOWNLOAD_BASE="${HOME}/.cache/lxc"
261 else
262 DOWNLOAD_BASE="${LOCALSTATEDIR}/cache/lxc"
263 fi
52e31c07 264else
4328e9e3 265 DOWNLOAD_BASE=/tmp
0ef43a5c 266fi
8c7536ec 267mkdir -p "${DOWNLOAD_BASE}"
0ef43a5c
SH
268
269# Trap all exit signals
270trap cleanup EXIT HUP INT TERM
271
4328e9e3
CB
272if ! which mktemp >/dev/null 2>&1; then
273 DOWNLOAD_TEMP="${DOWNLOAD_BASE}/lxc-oci.$$"
274 mkdir -p "${DOWNLOAD_TEMP}"
0ef43a5c 275else
4328e9e3 276 DOWNLOAD_TEMP=$(mktemp -d -p "${DOWNLOAD_BASE}")
0ef43a5c
SH
277fi
278
52e31c07 279# Download the image
4328e9e3 280# shellcheck disable=SC2039
797f99c6
FA
281skopeo_args=("")
282if [ -n "$OCI_USERNAME" ]; then
4328e9e3
CB
283 CREDENTIALS="${OCI_USERNAME}"
284
285 if [ -n "$OCI_PASSWORD" ]; then
286 CREDENTIALS="${CREDENTIALS}:${OCI_PASSWORD}"
287 fi
288
289 # shellcheck disable=SC2039
290 skopeo_args+=(--src-creds "${CREDENTIALS}")
797f99c6 291fi
4328e9e3 292
52e31c07 293if [ "${OCI_USE_CACHE}" = "true" ]; then
4328e9e3
CB
294 # shellcheck disable=SC2039
295 # shellcheck disable=SC2068
296 skopeo_args+=(--dest-shared-blob-dir "${DOWNLOAD_BASE}")
297 # shellcheck disable=SC2039
298 # shellcheck disable=SC2068
299 skopeo copy ${skopeo_args[@]} "${OCI_URL}" "oci:${DOWNLOAD_TEMP}:latest"
300 ln -s "${DOWNLOAD_BASE}/sha256" "${DOWNLOAD_TEMP}/blobs/sha256"
52e31c07 301else
4328e9e3
CB
302 # shellcheck disable=SC2039
303 # shellcheck disable=SC2068
304 skopeo copy ${skopeo_args[@]} "${OCI_URL}" "oci:${DOWNLOAD_TEMP}:latest"
52e31c07 305fi
0ef43a5c 306
0ef43a5c 307echo "Unpacking the rootfs"
4328e9e3 308# shellcheck disable=SC2039
51c80577
FA
309umoci_args=("")
310if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then
4328e9e3
CB
311 # shellcheck disable=SC2039
312 umoci_args+=(--rootless)
51c80577 313fi
4328e9e3
CB
314# shellcheck disable=SC2039
315# shellcheck disable=SC2068
845ba283 316umoci --log=error unpack ${umoci_args[@]} --image "${DOWNLOAD_TEMP}:latest" "${LXC_ROOTFS}.tmp"
0ef43a5c
SH
317rmdir "${LXC_ROOTFS}"
318mv "${LXC_ROOTFS}.tmp/rootfs" "${LXC_ROOTFS}"
0ef43a5c 319
4328e9e3 320OCI_CONF_FILE=$(getconfigpath "${DOWNLOAD_TEMP}" latest)
0ef43a5c 321LXC_CONF_FILE="${LXC_PATH}/config"
4328e9e3 322entrypoint=$(getep "${OCI_CONF_FILE}")
3dca1af0 323echo "lxc.execute.cmd = '${entrypoint}'" >> "${LXC_CONF_FILE}"
0ef43a5c
SH
324echo "lxc.mount.auto = proc:mixed sys:mixed cgroup:mixed" >> "${LXC_CONF_FILE}"
325
4328e9e3
CB
326environment=$(getenv "${OCI_CONF_FILE}")
327# shellcheck disable=SC2039
996202e7 328while read -r line; do
4328e9e3 329 echo "lxc.environment = ${line}" >> "${LXC_CONF_FILE}"
996202e7
FA
330done <<< "${environment}"
331
d7c685c6 332if [ -e "${LXC_TEMPLATE_CONFIG}/common.conf" ]; then
4328e9e3 333 echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/common.conf" >> "${LXC_CONF_FILE}"
d7c685c6
FA
334fi
335
336if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ] && [ -e "${LXC_TEMPLATE_CONFIG}/userns.conf" ]; then
4328e9e3 337 echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/userns.conf" >> "${LXC_CONF_FILE}"
d7c685c6
FA
338fi
339
1689c7cf 340if [ -e "${LXC_TEMPLATE_CONFIG}/oci.common.conf" ]; then
4328e9e3 341 echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/oci.common.conf" >> "${LXC_CONF_FILE}"
1689c7cf
JC
342fi
343
9a962dc6 344if [ "${OCI_USE_DHCP}" = "true" ]; then
4328e9e3
CB
345 echo "lxc.hook.start-host = ${LXC_HOOK_DIR}/dhclient" >> "${LXC_CONF_FILE}"
346 echo "lxc.hook.stop = ${LXC_HOOK_DIR}/dhclient" >> "${LXC_CONF_FILE}"
9a962dc6
JC
347fi
348
bc2c91ae
FA
349echo "lxc.uts.name = ${LXC_NAME}" >> "${LXC_CONF_FILE}"
350# set the hostname
4328e9e3 351cat <<EOF > "${LXC_ROOTFS}/etc/hostname"
bc2c91ae
FA
352${LXC_NAME}
353EOF
0ef43a5c 354
b5236550 355# set minimal hosts
4328e9e3 356cat <<EOF > "${LXC_ROOTFS}/etc/hosts"
b5236550
FA
357127.0.0.1 localhost
358127.0.1.1 ${LXC_NAME}
8f54d926
FA
359::1 localhost ip6-localhost ip6-loopback
360fe00::0 ip6-localnet
361ff00::0 ip6-mcastprefix
362ff02::1 ip6-allnodes
363ff02::2 ip6-allrouters
b5236550
FA
364EOF
365
4328e9e3
CB
366# shellcheck disable=SC2039
367uidgid=($(getuidgid "${OCI_CONF_FILE}"))
368# shellcheck disable=SC2039
e86dcc91 369echo "lxc.init.uid = ${uidgid[0]}" >> "${LXC_CONF_FILE}"
4328e9e3 370# shellcheck disable=SC2039
e86dcc91
FA
371echo "lxc.init.gid = ${uidgid[1]}" >> "${LXC_CONF_FILE}"
372
4328e9e3 373cwd=$(getcwd "${OCI_CONF_FILE}")
a787c332
FA
374echo "lxc.init.cwd = ${cwd}" >> "${LXC_CONF_FILE}"
375
0ef43a5c 376if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then
4328e9e3 377 chown "$LXC_MAPPED_UID" "$LXC_PATH/config" "$LXC_PATH/fstab" > /dev/null 2>&1 || true
0ef43a5c
SH
378fi
379if [ -n "$LXC_MAPPED_GID" ] && [ "$LXC_MAPPED_GID" != "-1" ]; then
4328e9e3 380 chgrp "$LXC_MAPPED_GID" "$LXC_PATH/config" "$LXC_PATH/fstab" > /dev/null 2>&1 || true
0ef43a5c
SH
381fi
382
383exit 0