]> git.proxmox.com Git - mirror_lxc.git/blob - templates/lxc-alpine.in
lxc-alpine: mount tmpfs on /dev/shm
[mirror_lxc.git] / templates / lxc-alpine.in
1 #!/bin/bash
2
3 key_sha256sums="9c102bcc376af1498d549b77bdbfa815ae86faa1d2d82f040e616b18ef2df2d4 alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub
4 2adcf7ce224f476330b5360ca5edb92fd0bf91c92d83292ed028d7c4e26333ab alpine-devel@lists.alpinelinux.org-4d07755e.rsa.pub"
5
6 get_static_apk () {
7 wget="wget -q -O -"
8 pkglist=alpine-keys:apk-tools-static
9 auto_repo_dir=
10
11 if [ -z "$repository" ]; then
12 url=http://wiki.alpinelinux.org/cgi-bin/dl.cgi
13 if [ -z "$release" ]; then
14 echo -n "Determining the latest release... "
15 release=$($wget $url/.latest.$apk_arch.txt | \
16 cut -d " " -f 3 | cut -d / -f 1 | uniq)
17 if [ -z "$release" ]; then
18 echo failed
19 return 1
20 fi
21 echo $release
22 fi
23 auto_repo_dir=$release/main
24 repository=$url/$auto_repo_dir
25 pkglist=$pkglist:alpine-mirrors
26 fi
27
28 rootfs="$1"
29 echo "Using static apk from $repository/$apk_arch"
30 wget="$wget $repository/$apk_arch"
31
32 # parse APKINDEX to find the current versions
33 static_pkgs=$($wget/APKINDEX.tar.gz | \
34 tar -Oxz APKINDEX | \
35 awk -F: -v pkglist=$pkglist '
36 BEGIN { split(pkglist,pkg) }
37 $0 != "" { f[$1] = $2 }
38 $0 == "" { for (i in pkg)
39 if (pkg[i] == f["P"])
40 print(f["P"] "-" f["V"] ".apk") }')
41 [ "$static_pkgs" ] || return 1
42
43 mkdir -p "$rootfs" || return 1
44 for pkg in $static_pkgs; do
45 echo "Downloading $pkg"
46 $wget/$pkg | tar -xz -C "$rootfs"
47 done
48
49 # clean up .apk meta files
50 rm -f "$rootfs"/.[A-Z]*
51
52 # verify checksum of the key
53 keyname=$(echo $rootfs/sbin/apk.static.*.pub | sed 's/.*\.SIGN\.RSA\.//')
54 checksum=$(echo "$key_sha256sums" | grep -w "$keyname")
55 if [ -z "$checksum" ]; then
56 echo "ERROR: checksum is missing for $keyname"
57 return 1
58 fi
59 (cd $rootfs/etc/apk/keys && echo "$checksum" | sha256sum -c -) || return 1
60
61 # verify the static apk binary signature
62 APK=$rootfs/sbin/apk.static
63 openssl dgst -verify $rootfs/etc/apk/keys/$keyname \
64 -signature "$APK.SIGN.RSA.$keyname" "$APK" || return 1
65
66 if [ "$auto_repo_dir" ]; then
67 mirror_list=$rootfs/usr/share/alpine-mirrors/MIRRORS.txt
68 mirror_count=$(wc -l $mirror_list | cut -d " " -f 1)
69 repository=$(sed $(expr $RANDOM % $mirror_count + 1)\!d \
70 $mirror_list)$auto_repo_dir
71 echo "Selecting mirror $repository"
72 fi
73 }
74
75 install_alpine() {
76 rootfs="$1"
77 shift
78 mkdir -p "$rootfs"/etc/apk || return 1
79 : ${keys_dir:=/etc/apk/keys}
80 if ! [ -d "$rootfs"/etc/apk/keys ] && [ -d "$keys_dir" ]; then
81 cp -r "$keys_dir" "$rootfs"/etc/apk/keys
82 fi
83 if [ -n "$repository" ]; then
84 echo "$repository" > "$rootfs"/etc/apk/repositories
85 else
86 cp /etc/apk/repositories "$rootfs"/etc/apk/repositories || return 1
87 if [ -n "$release" ]; then
88 sed -i -e "s:/[^/]\+/\([^/]\+\)$:/$release/\1:" \
89 "$rootfs"/etc/apk/repositories
90 fi
91 fi
92 opt_arch=
93 if [ -n "$apk_arch" ]; then
94 opt_arch="--arch $apk_arch"
95 fi
96 $APK add -U --initdb --root $rootfs $opt_arch "$@" alpine-base
97 }
98
99 configure_alpine() {
100 rootfs="$1"
101 echo "Setting up /etc/inittab"
102 cat >"$rootfs"/etc/inittab<<EOF
103 ::sysinit:/sbin/rc sysinit
104 ::wait:/sbin/rc default
105 tty1:12345:respawn:/sbin/getty 38400 tty1
106 tty2:12345:respawn:/sbin/getty 38400 tty2
107 tty3:12345:respawn:/sbin/getty 38400 tty3
108 tty4:12345:respawn:/sbin/getty 38400 tty4
109 ::ctrlaltdel:/sbin/reboot
110 ::shutdown:/sbin/rc shutdown
111 EOF
112 # set up timezone
113 if [ -f /etc/TZ ]; then
114 cp /etc/TZ "$rootfs/etc/TZ"
115 fi
116
117 # set up nameserver
118 grep nameserver /etc/resolv.conf > "$rootfs/etc/resolv.conf"
119
120 # configure the network using the dhcp
121 cat <<EOF > $rootfs/etc/network/interfaces
122 auto lo
123 iface lo inet loopback
124
125 auto eth0
126 iface eth0 inet dhcp
127 EOF
128
129 # set the hostname
130 echo $hostname > $rootfs/etc/hostname
131
132 # missing device nodes
133 echo "Setting up device nodes"
134 mkdir -p -m 755 "$rootfs/dev/pts"
135 mkdir -p -m 1777 "$rootfs/dev/shm"
136 mknod -m 666 "$rootfs/dev/zero" c 1 5
137 mknod -m 666 "$rootfs/dev/full" c 1 7
138 mknod -m 666 "$rootfs/dev/random" c 1 8
139 mknod -m 666 "$rootfs/dev/urandom" c 1 9
140 mknod -m 666 "$rootfs/dev/tty0" c 4 0
141 mknod -m 666 "$rootfs/dev/tty1" c 4 1
142 mknod -m 666 "$rootfs/dev/tty2" c 4 2
143 mknod -m 666 "$rootfs/dev/tty3" c 4 3
144 mknod -m 666 "$rootfs/dev/tty4" c 4 4
145 # mknod -m 600 "$rootfs/dev/initctl" p
146 mknod -m 666 "$rootfs/dev/tty" c 5 0
147 mknod -m 666 "$rootfs/dev/console" c 5 1
148 mknod -m 666 "$rootfs/dev/ptmx" c 5 2
149
150 # start services
151 ln -s /etc/init.d/bootmisc "$rootfs"/etc/runlevels/boot/bootmisc
152 ln -s /etc/init.d/syslog "$rootfs"/etc/runlevels/boot/syslog
153
154 return 0
155 }
156
157 copy_configuration() {
158 path=$1
159 rootfs=$2
160 hostname=$3
161
162 grep -q "^lxc.rootfs" $path/config 2>/dev/null \
163 || echo "lxc.rootfs = $rootfs" >> $path/config
164 if [ -n "$lxc_arch" ]; then
165 echo "lxc.arch = $lxc_arch" >> $path/config
166 fi
167
168 lxc_network_link_line="# lxc.network.link = br0"
169 for br in lxcbr0 virbr0 br0; do
170 if [ -d /sys/class/net/$br/bridge ]; then
171 lxc_network_link_line="lxc.network.link = $br"
172 break
173 fi
174 done
175
176 if ! grep -q "^lxc.network.type" $path/config 2>/dev/null; then
177 cat <<EOF >> $path/config
178 lxc.network.type = veth
179 $lxc_network_link_line
180 lxc.network.flags = up
181 EOF
182 fi
183
184 # if there is exactly one veth or macvlan network entry, make sure
185 # it has an associated mac address.
186 nics=$(awk -F '[ \t]*=[ \t]*' \
187 '$1=="lxc.network.type" && ($2=="veth" || $2=="macvlan") {print $2}' \
188 $path/config | wc -l)
189 if [ "$nics" -eq 1 ] && ! grep -q "^lxc.network.hwaddr" $path/config; then
190 # see http://sourceforge.net/tracker/?func=detail&aid=3411497&group_id=163076&atid=826303
191 hwaddr="fe:$(dd if=/dev/urandom bs=8 count=1 2>/dev/null |od -t x8 | \
192 head -1 |awk '{print $2}' | cut -c1-10 |\
193 sed 's/\(..\)/\1:/g; s/.$//')"
194 echo "lxc.network.hwaddr = $hwaddr" >> $path/config
195 fi
196
197 cat <<EOF >> $path/config
198
199 lxc.tty = 4
200 lxc.pts = 1024
201 lxc.utsname = $hostname
202 lxc.cap.drop = sys_module mac_admin mac_override sys_time
203
204 # When using LXC with apparmor, uncomment the next line to run unconfined:
205 #lxc.aa_profile = unconfined
206
207 # devices
208 lxc.cgroup.devices.deny = a
209 # /dev/null, zero and full
210 lxc.cgroup.devices.allow = c 1:3 rwm
211 lxc.cgroup.devices.allow = c 1:5 rwm
212 lxc.cgroup.devices.allow = c 1:7 rwm
213 # consoles
214 lxc.cgroup.devices.allow = c 5:1 rwm
215 lxc.cgroup.devices.allow = c 5:0 rwm
216 lxc.cgroup.devices.allow = c 4:0 rwm
217 lxc.cgroup.devices.allow = c 4:1 rwm
218 # /dev/{,u}random
219 lxc.cgroup.devices.allow = c 1:9 rwm
220 lxc.cgroup.devices.allow = c 1:8 rwm
221 lxc.cgroup.devices.allow = c 136:* rwm
222 lxc.cgroup.devices.allow = c 5:2 rwm
223 # rtc
224 lxc.cgroup.devices.allow = c 254:0 rm
225
226 # mounts point
227 lxc.mount.entry=proc proc proc nodev,noexec,nosuid 0 0
228 lxc.mount.entry=run run tmpfs nodev,noexec,nosuid,relatime,size=1m,mode=0755 0 0
229 lxc.mount.entry=none dev/pts devpts gid=5,mode=620 0 0
230 lxc.mount.entry=shm dev/shm tmpfs nodev,nosuid,noexec 0 0
231
232 EOF
233
234 return 0
235 }
236
237 die() {
238 echo "$@" >&2
239 exit 1
240 }
241
242 usage() {
243 cat >&2 <<EOF
244 Usage: $(basename $0) [-h|--help] [-r|--repository <url>]
245 [-R|--release <release>] [-a|--arch <arch>]
246 [--rootfs <rootfs>] -p|--path <path> -n|--name <name>
247 [PKG...]
248 EOF
249 }
250
251 usage_err() {
252 usage
253 exit 1
254 }
255
256 optarg_check() {
257 if [ -z "$2" ]; then
258 usage_err "option '$1' requires an argument"
259 fi
260 }
261
262 default_path=@LXCPATH@
263 release=
264 arch=$(uname -m)
265
266 # template mknods, requires root
267 if [ $(id -u) -ne 0 ]; then
268 echo "$(basename $0): must be run as root" >&2
269 exit 1
270 fi
271
272 while [ $# -gt 0 ]; do
273 opt="$1"
274 shift
275 case "$opt" in
276 -h|--help)
277 usage
278 exit 0
279 ;;
280 -n|--name)
281 optarg_check $opt "$1"
282 name=$1
283 shift
284 ;;
285 --rootfs)
286 optarg_check $opt "$1"
287 rootfs=$1
288 shift
289 ;;
290 -p|--path)
291 optarg_check $opt "$1"
292 path=$1
293 shift
294 ;;
295 -r|--repository)
296 optarg_check $opt "$1"
297 repository=$1
298 shift
299 ;;
300 -R|--release)
301 optarg_check $opt "$1"
302 release=$1
303 shift
304 ;;
305 -a|--arch)
306 optarg_check $opt "$1"
307 arch=$1
308 shift
309 ;;
310 --)
311 break;;
312 --*=*)
313 # split --myopt=foo=bar into --myopt foo=bar
314 set -- ${opt%=*} ${opt#*=} "$@"
315 ;;
316 -?)
317 usage_err "unknown option '$opt'"
318 ;;
319 -*)
320 # split opts -abc into -a -b -c
321 set -- $(echo "${opt#-}" | sed 's/\(.\)/ -\1/g') "$@"
322 ;;
323 esac
324 done
325
326
327 [ -z "$name" ] && usage_err
328
329 if [ -z "${path}" ]; then
330 path="${default_path}/${name}"
331 fi
332
333 if [ -z "$rootfs" ]; then
334 rootfs=`awk -F= '$1 ~ /^lxc.rootfs/ { print $2 }' "$path/config" 2>/dev/null`
335 if [ -z "$rootfs" ]; then
336 rootfs="${path}/rootfs"
337 fi
338 fi
339
340 lxc_arch=$arch
341 apk_arch=$arch
342
343 case "$arch" in
344 i[3-6]86)
345 apk_arch=x86
346 ;;
347 x86)
348 lxc_arch=i686
349 ;;
350 x86_64|"")
351 ;;
352 *)
353 die "unsupported architecture: $arch"
354 ;;
355 esac
356
357 : ${APK:=apk}
358 if ! which $APK >/dev/null; then
359 get_static_apk "$rootfs" || die "Failed to download a valid static apk"
360 fi
361
362 install_alpine "$rootfs" "$@" || die "Failed to install rootfs for $name"
363 configure_alpine "$rootfs" "$name" || die "Failed to configure $name"
364 copy_configuration "$path" "$rootfs" "$name"