]> git.proxmox.com Git - mirror_lxc.git/blob - templates/lxc-sabayon.in
Merge pull request #1465 from geaaru/lxc-sabayon-unpriv
[mirror_lxc.git] / templates / lxc-sabayon.in
1 #!/bin/bash
2 # vim: set ts=4 sw=4 expandtab
3
4 # Exit on error and treat unset variables as an error.
5 set -eu
6
7 #
8 # LXC template for Sabayon OS, based of Alpine script.
9 #
10
11 # Authors:
12 # Geaaru <geaaru@gmail.com>
13
14 # This library is free software; you can redistribute it and/or
15 # modify it under the terms of the GNU Lesser General Public
16 # License as published by the Free Software Foundation; either
17 # version 2.1 of the License, or (at your option) any later version.
18 #
19 # This library is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 # Lesser General Public License for more details.
23 #
24 # You should have received a copy of the GNU Lesser General Public
25 # License along with this library; if not, write to the Free Software
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27
28
29 #=========================== Constants ============================#
30
31 # Make sure the usual locations are in PATH
32 export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin
33
34 readonly LOCAL_STATE_DIR='@LOCALSTATEDIR@'
35 readonly LXC_TEMPLATE_CONFIG='@LXCTEMPLATECONFIG@'
36
37
38 # Temporary static MIRROR LIST. I will get list from online path on the near future.
39 readonly MIRRORS_LIST="
40 http://mirror.it.sabayon.org/
41 http://dl.sabayon.org/
42 http://ftp.kddilabs.jp/Linux/packages/sabayonlinux/
43 ftp://ftp.klid.dk/sabayonlinux/
44 http://ftp.fsn.hu/pub/linux/distributions/sabayon/
45 http://ftp.cc.uoc.gr/mirrors/linux/SabayonLinux/
46 http://ftp.rnl.ist.utl.pt/pub/sabayon/
47 ftp://ftp.nluug.nl/pub/os/Linux/distr/sabayonlinux/
48 http://ftp.surfnet.nl/pub/os/Linux/distr/sabayonlinux/
49 http://mirror.internode.on.net/pub/sabayon/
50 http://mirror.yandex.ru/sabayon/
51 http://sabayon.c3sl.ufpr.br/
52 http://mirror.clarkson.edu/sabayon/
53 http://na.mirror.garr.it/mirrors/sabayonlinux/"
54
55 #======================== Global variables ========================#
56
57 # Clean variables and set defaults.
58 arch="$(uname -m)"
59 debug='no'
60 flush_cache='no'
61 mirror_url=
62 name=
63 path=
64 release="DAILY"
65 rootfs=
66 unprivileged=false
67 mapped_uid=
68 mapped_gid=
69 flush_owner=false
70
71 #======================== Helper Functions ========================#
72
73 usage() {
74 cat <<-EOF
75 Template specific options can be passed to lxc-create after a '--' like this:
76
77 lxc-create --name=NAME [lxc-create-options] -- [template-options]
78
79 Template options:
80 -a ARCH, --arch=ARCH The container architecture (e.g. x86_64, armv7); defaults
81 to the host arch.
82 -d, --debug Run this script in a debug mode (set -x and wget w/o -q).
83 -m URL --mirror=URL The Sabayon mirror to use; defaults to random mirror.
84 -u, --unprivileged Tuning of rootfs for unprivileged containers.
85 -r, --release Identify release to use. Default is DAILY.
86 --mapped-gid Group Id to use on unprivileged container
87 (based of value present on file /etc/subgid).
88 --mapped-uid User Id to use on unprivileged container
89 (based of value present on file /etc/subuid)
90 --flush-owner Only for directly creation of unprivileged containers
91 through lxc-create command. Execute fuidshift command.
92 Require --mapped-gid,--mapped-uid and --unprivileged
93 options.
94
95 Environment variables:
96 RELEASE Release version of Sabayon. Default is ${RELEASE}.
97 EOF
98 }
99
100 random_mirror_url() {
101 local url=""
102
103 if [ $arch == 'amd64' ] ; then
104 url=$(echo $MIRRORS_LIST | sed -e 's/ /\n/g' | sort -R --random-source=/dev/urandom | head -n 1)
105 else
106 if [ $arch == 'armv7l' ] ; then
107 # Currently armv7l tarball is not on sabayon mirrored tree.
108 url="https://dockerbuilder.sabayon.org/"
109 fi
110 fi
111
112 [ -n "$url" ] && echo "$url"
113 }
114
115 die() {
116 local retval=$1; shift
117
118 echo -e "==> $@\n"
119 exit $retval
120 }
121
122 einfo() {
123 echo -e "==> $@\n"
124 }
125
126 fetch() {
127 if [ "$debug" = 'yes' ]; then
128 wget -T 10 -O - $@
129 else
130 wget -T 10 -O - -q $@
131 fi
132 }
133
134 parse_arch() {
135 case "$1" in
136 x86_64 | amd64) echo 'amd64';;
137 armv7 | armv7l) echo 'armv7l';;
138 #arm*) echo 'armhf';;
139 *) return 1;;
140 esac
141 }
142
143 run_exclusively() {
144
145 local lock_name="$1"
146 local timeout=$2
147 local method=$3
148 shift 3
149
150 mkdir -p "$LOCAL_STATE_DIR/lock/subsys"
151
152 local retval
153 {
154 echo -n "Obtaining an exclusive lock..."
155 if ! flock -x 9; then
156 echo ' failed.'
157 return 1
158 fi
159 echo ' done'
160
161 ${method} $@
162 retval=$?
163 } 9> "$LOCAL_STATE_DIR/lock/subsys/lxc-sabayon-$lock_name"
164
165 return $retval
166 }
167
168 create_url () {
169
170 local url=""
171 # Example of amd64 tarball url
172 # http://mirror.yandex.ru/sabayon/iso/daily/Sabayon_Linux_DAILY_amd64_tarball.tar.gz
173
174 if [ $arch == 'amd64' ] ; then
175
176 if [ $release = 'DAILY' ] ; then
177 url="${MIRROR_URL}iso/daily/Sabayon_Linux_DAILY_amd64_tarball.tar.gz"
178 else
179 url="${MIRROR_URL}iso/monthly/Sabayon_Linux_${release}_amd64_tarball.tar.gz"
180 fi
181 else
182 # https://dockerbuilder.sabayon.org/Sabayon_Linux_16_armv7l.tar.bz2
183 if [ $arch == 'armv7l' ] ; then
184
185 # Currently $arch tarball is not on sabayon mirrored tree.
186 url="${MIRROR_URL}Sabayon_Linux_16_armv7l.tar.bz2"
187
188 fi
189 fi
190
191 echo $url
192 }
193
194
195 #=========================== Configure ===========================#
196
197 unprivileged_rootfs() {
198
199 pushd ${rootfs}/etc/systemd/system
200
201 # Disable systemd-journald-audit.socket because it seems that doesn't
202 # start correctly on unprivileged container
203 ln -s /dev/null systemd-journald-audit.socket
204
205 # Disable systemd-remount-fs.service because on unprivileged container
206 # systemd can't remount filesystem
207 ln -s /dev/null systemd-remount-fs.service
208
209 # Remove mount of FUSE Control File system
210 ln -s /dev/null sys-fs-fuse-connections.mount
211
212 # Change execution of service systemd-sysctl to avoid errors.
213 mkdir systemd-sysctl.service.d
214 cat <<EOF > systemd-sysctl.service.d/00gentoo.conf
215 [Service]
216 ExecStart=
217 ExecStart=/usr/lib/systemd/systemd-sysctl --prefix=/etc/sysctl.d/
218 EOF
219
220 # Disable mount of hugepages
221 ln -s /dev/null dev-hugepages.mount
222
223 popd
224
225 pushd ${rootfs}
226
227 # Disable sabayon-anti-fork-bomb limits (already apply to lxc container manager)
228 sed -i -e 's/^*/#*/g' ./etc/security/limits.d/00-sabayon-anti-fork-bomb.conf || return 1
229 sed -i -e 's/^root/#root/g' ./etc/security/limits.d/00-sabayon-anti-fork-bomb.conf || return 1
230
231 popd
232
233 return 0
234 }
235
236 unprivileged_shift_owner () {
237
238 # I use /usr/bin/fuidshift from LXD project.
239
240 einfo "Executing: fuidshift ${rootfs} u:0:${mapped_uid}:65536 g:0:${mapped_gid}:65536 ..."
241
242 fuidshift ${rootfs} u:0:${mapped_uid}:65536 g:0:${mapped_gid}:65536 ||
243 die 1 "Error on change owners of ${rootfs} directory"
244
245 einfo "Done."
246
247 # Fix permission of container directory
248 chmod a+rx ${path}
249
250 return 0
251 }
252
253 systemd_container_tuning () {
254
255 # To avoid error on start systemd-tmpfiles-setup service
256 # it is needed clean journal directory
257 rm -rf ${rootfs}/var/log/journal/
258
259 # Remove LVM service. Normally not needed on container system.
260 rm -rf ${rootfs}/etc/systemd/system/sysinit.target.wants/lvm2-lvmetad.service
261
262 # Comment unneeded entry on /etc/fstab
263 sed -e 's/\/dev/#\/dev/g' -i ${rootfs}/etc/fstab
264
265 # Fix this stupid error until fix is available on sabayon image
266 # /usr/lib/systemd/system-generators/gentoo-local-generator: line 4: cd: /etc/local.d: No such file or directory
267 mkdir ${rootfs}/etc/local.d/
268
269 # Fix TERM variable for container console
270 mkdir container-getty\@0.service.d
271 cat <<EOF > container-getty\@0.service.d/00gentoo.conf
272 [Service]
273 Environment=TERM=
274 Environment=TERM=linux
275 EOF
276
277 return 0
278 }
279
280 configure_container() {
281 local config="$1"
282 local hostname="$2"
283 local arch="$3"
284 local privileged_options=""
285 local unprivileged_options=""
286
287 if [[ $unprivileged && $unprivileged == true ]] ; then
288 if [[ $flush_owner == true ]] ; then
289 unprivileged_options="
290 lxc.id_map = u 0 ${mapped_uid} 65536
291 lxc.id_map = g 0 ${mapped_gid} 65536
292 "
293 fi
294
295 unprivileged_options="
296 $unprivileged_options
297
298 # Include common configuration.
299 lxc.include = $LXC_TEMPLATE_CONFIG/sabayon.userns.conf
300 "
301
302 else
303 privileged_options="
304 ## Allow any mknod (but not reading/writing the node)
305 lxc.cgroup.devices.allow = b *:* m
306 lxc.cgroup.devices.allow = c *:* m
307
308 ### /dev/pts/*
309 lxc.cgroup.devices.allow = c 136:* rwm
310 ### /dev/tty
311 lxc.cgroup.devices.allow = c 5:0 rwm
312 ### /dev/console
313 lxc.cgroup.devices.allow = c 5:1 rwm
314 ### /dev/ptmx
315 lxc.cgroup.devices.allow = c 5:2 rwm
316 ### fuse
317 lxc.cgroup.devices.allow = c 10:229 rwm
318
319 "
320 fi
321
322 cat <<-EOF >> "$config"
323 # Specify container architecture.
324 lxc.arch = $arch
325
326 # Set hostname.
327 lxc.utsname = $hostname
328
329 # Include common configuration.
330 lxc.include = $LXC_TEMPLATE_CONFIG/sabayon.common.conf
331
332 $unprivileged_options
333 $privileged_options
334 EOF
335 }
336
337
338 #============================= Main ==============================#
339
340 parse_cmdline() {
341
342 # Parse command options.
343 local short_options="a:dm:n:p:r:hu"
344 local long_options="arch:,debug,mirror:,name:,path:,release:,rootfs:,mapped-uid:,mapped-gid:,flush-owner,help"
345
346 options=$(getopt -u -q -a -o "$short_options" -l "$long_options" -- "$@")
347
348 eval set -- "$options"
349
350 # Process command options.
351 while [ $# -gt 0 ]; do
352 case $1 in
353 -a | --arch)
354 arch=$2
355 shift
356 ;;
357 -d | --debug)
358 debug='yes'
359 ;;
360 -m | --mirror)
361 mirror_url=$2
362 shift
363 ;;
364 -n | --name)
365 name=$2
366 shift
367 ;;
368 -p | --path)
369 path=$2
370 shift
371 ;;
372 -r | --release)
373 release=$2
374 shift
375 ;;
376 --rootfs)
377 rootfs=$2
378 shift
379 ;;
380 -u | --unprivileged)
381 unprivileged=true
382 ;;
383 -h | --help)
384 usage
385 exit 1
386 ;;
387 --mapped-uid)
388 mapped_uid=$2
389 shift
390 ;;
391 --mapped-gid)
392 mapped_gid=$2
393 shift
394 ;;
395 --flush-owner)
396 flush_owner=true
397 ;;
398 --)
399 break
400 ;;
401 *)
402 einfo "Unknown option: $1"
403 usage
404 exit 1
405 ;;
406 esac
407 shift
408 done
409
410 if [ "$(id -u)" != "0" ]; then
411 die 1 "This script must be run as 'root'"
412 fi
413
414 # Validate options.
415 [ -n "$name" ] || die 1 'Missing required option --name'
416 [ -n "$path" ] || die 1 'Missing required option --path'
417
418 if [ -z "$rootfs" ] && [ -f "$path/config" ]; then
419 rootfs="$(sed -nE 's/^lxc.rootfs\s*=\s*(.*)$/\1/p' "$path/config")"
420 fi
421 if [ -z "$rootfs" ]; then
422 rootfs="$path/rootfs"
423 fi
424
425 [ -z "$path" ] && die 1 "'path' parameter is required."
426
427 arch=$(parse_arch "$arch") \
428 || die 1 "Unsupported architecture: $arch"
429
430 [[ $unprivileged == true && $flush_owner == true &&-z "$mapped_uid" ]] && \
431 die 1 'Missing required option --mapped-uid with --unprivileged option'
432
433 [[ $unprivileged == true && $flush_owner == true && -z "$mapped_gid" ]] && \
434 die 1 'Missing required option --mapped-gid with --unprivileged option'
435
436 [[ $flush_owner == true && $unprivileged == false ]] && \
437 die 1 'flush-owner require --unprivileged option'
438
439 return 0
440 }
441
442 main () {
443
444 local tarball=""
445
446 # Set global variables.
447 RELEASE="${RELEASE:-"DAILY"}"
448 ARCH="${ARCH:-`uname -m`}"
449 OS="${OS:-"sabayon"}"
450
451 einfo "Processing command line arguments: $@"
452
453 # Parse command line options
454 parse_cmdline "$@"
455
456 DEBUG="$debug"
457 MIRROR_URL="${mirror_url:-$(random_mirror_url)}"
458
459 einfo "Use arch = $arch, mirror_url = $MIRROR_URL, path = $path, name = $name, release = $release, unprivileged = $unprivileged, rootfs = $rootfs, mapped_uid = $mapped_uid, mapped_gid = $mapped_gid, flush_owner = $flush_owner"
460
461 [ "$debug" = 'yes' ] && set -x
462
463 # Download sabayon tarball
464 tarball=$(create_url)
465 einfo "Fetching tarball $tarball..."
466
467 # TODO: use only a compression mode
468 if [ $arch == 'amd64' ] ; then
469 fetch "${tarball}" | tar -xpz -C "${rootfs}"
470 else
471 if [ $arch == 'armv7l' ] ; then
472 fetch "${tarball}" | tar -xpj -C "${rootfs}"
473 fi
474 fi
475
476 einfo "Tarball ${tarball} Extracted."
477
478 systemd_container_tuning
479
480 # Fix container for unprivileged mode.
481 if [[ $unprivileged == true ]] ; then
482 unprivileged_rootfs
483 if [[ $flush_owner == true ]] ; then
484 unprivileged_shift_owner
485 fi
486 fi
487
488 return 0
489 }
490
491
492 einfo "Prepare creation of sabayon container with params: $@ ($#)"
493
494 # Here we go!
495 run_exclusively 'main' 10 main "$@"
496 configure_container "$path/config" "$name" "$arch"
497
498 einfo "Container's rootfs and config have been created"
499 cat <<-EOF
500 Edit the config file $path/config to check/enable networking setup.
501 The installed system is preconfigured for a loopback and single network
502 interface configured via DHCP.
503
504 To start the container, run "lxc-start -n $name".
505 The root password is not set; to enter the container run "lxc-attach -n $name".
506
507 Note: From kenel >= 4.6 for use unprivileged containers it is needed this mount on host:
508
509 mkdir /sys/fs/cgroup/systemd
510 mount -t cgroup -o none,name=systemd systemd /sys/fs/cgroup/systemd
511 EOF