]>
Commit | Line | Data |
---|---|---|
164105f6 MW |
1 | #!/bin/bash |
2 | ||
3 | # | |
491a01cf | 4 | # template script for generating CentOS container for LXC |
164105f6 MW |
5 | |
6 | # | |
7 | # lxc: linux Container library | |
8 | ||
9 | # Authors: | |
10 | # Daniel Lezcano <daniel.lezcano@free.fr> | |
11 | # Ramez Hanna <rhanna@informatiq.org> | |
12 | # Fajar A. Nugraha <github@fajar.net> | |
13 | # Michael H. Warfield <mhw@WittsEnd.com> | |
14 | ||
15 | # This library is free software; you can redistribute it and/or | |
16 | # modify it under the terms of the GNU Lesser General Public | |
17 | # License as published by the Free Software Foundation; either | |
18 | # version 2.1 of the License, or (at your option) any later version. | |
19 | ||
20 | # This library is distributed in the hope that it will be useful, | |
21 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
96283b54 | 22 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
164105f6 MW |
23 | # Lesser General Public License for more details. |
24 | ||
25 | # You should have received a copy of the GNU Lesser General Public | |
26 | # License along with this library; if not, write to the Free Software | |
27 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
28 | ||
29 | #Configurations | |
164105f6 | 30 | default_path=@LXCPATH@ |
164105f6 | 31 | |
826cde7c | 32 | # Some combinations of the tuning knobs below do not exactly make sense. |
b4f7af7a MW |
33 | # but that's ok. |
34 | # | |
35 | # If the "root_password" is non-blank, use it, else set a default. | |
36 | # This can be passed to the script as an environment variable and is | |
37 | # set by a shell conditional assignment. Looks weird but it is what it is. | |
38 | # | |
39 | # If the root password contains a ding ($) then try to expand it. | |
40 | # That will pick up things like ${name} and ${RANDOM}. | |
ec64264d | 41 | # If the root password contains more than 3 consecutive X's, pass it as |
b4f7af7a MW |
42 | # a template to mktemp and take the result. |
43 | # | |
44 | # If root_display_password = yes, display the temporary root password at exit. | |
45 | # If root_store_password = yes, store it in the configuration directory | |
46 | # If root_prompt_password = yes, invoke "passwd" to force the user to change | |
47 | # the root password after the container is created. | |
826cde7c MW |
48 | # If root_expire_password = yes, you will be prompted to change the root |
49 | # password at the first login. | |
b4f7af7a MW |
50 | # |
51 | # These are conditional assignments... The can be overridden from the | |
52 | # preexisting environment variables... | |
53 | # | |
54 | # Make sure this is in single quotes to defer expansion to later! | |
55 | # :{root_password='Root-${name}-${RANDOM}'} | |
56 | : ${root_password='Root-${name}-XXXXXX'} | |
57 | ||
58 | # Now, it doesn't make much sense to display, store, and force change | |
59 | # together. But, we gotta test, right??? | |
60 | : ${root_display_password='no'} | |
61 | : ${root_store_password='yes'} | |
62 | # Prompting for something interactive has potential for mayhem | |
63 | # with users running under the API... Don't default to "yes" | |
64 | : ${root_prompt_password='no'} | |
65 | ||
826cde7c MW |
66 | # Expire root password? Default to yes, but can be overridden from |
67 | # the environment variable | |
68 | : ${root_expire_password='yes'} | |
69 | ||
b4f7af7a | 70 | # These are only going into comments in the resulting config... |
164105f6 MW |
71 | lxc_network_type=veth |
72 | lxc_network_link=lxcbr0 | |
73 | ||
491a01cf | 74 | # is this CentOS? |
164105f6 MW |
75 | # Alow for weird remixes like the Raspberry Pi |
76 | # | |
77 | # Use the Mitre standard CPE identifier for the release ID if possible... | |
78 | # This may be in /etc/os-release or /etc/system-release-cpe. We | |
79 | # should be able to use EITHER. Give preference to /etc/os-release for now. | |
80 | ||
8ec981fc | 81 | # Detect use under userns (unsupported) |
c63c04fc | 82 | for arg in "$@"; do |
96283b54 SG |
83 | [ "$arg" = "--" ] && break |
84 | if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then | |
8ec981fc SG |
85 | echo "This template can't be used for unprivileged containers." 1>&2 |
86 | echo "You may want to try the \"download\" template instead." 1>&2 | |
87 | exit 1 | |
88 | fi | |
89 | done | |
90 | ||
207bf0e4 SG |
91 | # Make sure the usual locations are in PATH |
92 | export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin | |
93 | ||
164105f6 MW |
94 | if [ -e /etc/os-release ] |
95 | then | |
96 | # This is a shell friendly configuration file. We can just source it. | |
97 | # What we're looking for in here is the ID, VERSION_ID and the CPE_NAME | |
98 | . /etc/os-release | |
99 | echo "Host CPE ID from /etc/os-release: ${CPE_NAME}" | |
100 | fi | |
101 | ||
102 | if [ "${CPE_NAME}" = "" -a -e /etc/system-release-cpe ] | |
103 | then | |
104 | CPE_NAME=$(head -n1 /etc/system-release-cpe) | |
c6df5ca4 | 105 | CPE_URI=$(expr ${CPE_NAME} : '\([^:]*:[^:]*\)') |
164105f6 MW |
106 | if [ "${CPE_URI}" != "cpe:/o" ] |
107 | then | |
108 | CPE_NAME= | |
109 | else | |
164105f6 MW |
110 | # Probably a better way to do this but sill remain posix |
111 | # compatible but this works, shrug... | |
112 | # Must be nice and not introduce convenient bashisms here. | |
c6df5ca4 MW |
113 | # |
114 | # According to the official registration at Mitre and NIST, | |
115 | # this should have been something like this for CentOS: | |
116 | # cpe:/o:centos:centos:6 | |
117 | # or this: | |
118 | # cpe:/o:centos:centos:6.5 | |
119 | # | |
164105f6 | 120 | ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:\([^:]*\)') |
c6df5ca4 MW |
121 | # The "enterprise_linux" is a bone toss back to RHEL. |
122 | # Since CentOS and RHEL are so tightly coupled, we'll | |
123 | # take the RHEL version if we're running on it and do the | |
124 | # equivalent version for CentOS. | |
125 | if [ ${ID} = "linux" -o ${ID} = "enterprise_linux" ] | |
126 | then | |
127 | # Instead we got this: cpe:/o:centos:linux:6 | |
128 | ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:\([^:]*\)') | |
129 | fi | |
130 | ||
164105f6 | 131 | VERSION_ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\)') |
c6df5ca4 | 132 | echo "Host CPE ID from /etc/system-release-cpe: ${CPE_NAME}" |
164105f6 MW |
133 | fi |
134 | fi | |
135 | ||
136 | if [ "${CPE_NAME}" != "" -a "${ID}" = "centos" -a "${VERSION_ID}" != "" ] | |
137 | then | |
138 | centos_host_ver=${VERSION_ID} | |
139 | is_centos=true | |
0395c47c | 140 | elif [ "${CPE_NAME}" != "" -a "${ID}" = "redhat" -o "${ID}" = "rhel" -a "${VERSION_ID}" != "" ] |
c6df5ca4 | 141 | then |
0395c47c | 142 | # RHEL 7+ /etc/os-release ID = 'rhel', which doesn't enter this elif without the added OR statement |
c6df5ca4 MW |
143 | redhat_host_ver=${VERSION_ID} |
144 | is_redhat=true | |
145 | elif [ -e /etc/centos-release ] | |
164105f6 MW |
146 | then |
147 | # Only if all other methods fail, try to parse the redhat-release file. | |
c6df5ca4 | 148 | centos_host_ver=$( sed -e '/^CentOS /!d' -e 's/CentOS.*\srelease\s*\([0-9][0-9.]*\)\s.*/\1/' < /etc/centos-release ) |
164105f6 MW |
149 | if [ "$centos_host_ver" != "" ] |
150 | then | |
151 | is_centos=true | |
152 | fi | |
153 | fi | |
154 | ||
164105f6 MW |
155 | force_mknod() |
156 | { | |
157 | # delete a device node if exists, and create a new one | |
158 | rm -f $2 && mknod -m $1 $2 $3 $4 $5 | |
159 | } | |
160 | ||
161 | configure_centos() | |
162 | { | |
163 | ||
491a01cf | 164 | # disable selinux in CentOS |
164105f6 MW |
165 | mkdir -p $rootfs_path/selinux |
166 | echo 0 > $rootfs_path/selinux/enforce | |
167 | ||
168 | # Also kill it in the /etc/selinux/config file if it's there... | |
17abf278 | 169 | if [ -f $rootfs_path/etc/selinux/config ] |
164105f6 MW |
170 | then |
171 | sed -i '/^SELINUX=/s/.*/SELINUX=disabled/' $rootfs_path/etc/selinux/config | |
172 | fi | |
173 | ||
174 | # Nice catch from Dwight Engen in the Oracle template. | |
175 | # Wantonly plagerized here with much appreciation. | |
176 | if [ -f $rootfs_path/usr/sbin/selinuxenabled ]; then | |
177 | mv $rootfs_path/usr/sbin/selinuxenabled $rootfs_path/usr/sbin/selinuxenabled.lxcorig | |
178 | ln -s /bin/false $rootfs_path/usr/sbin/selinuxenabled | |
179 | fi | |
180 | ||
181 | # This is a known problem and documented in RedHat bugzilla as relating | |
182 | # to a problem with auditing enabled. This prevents an error in | |
183 | # the container "Cannot make/remove an entry for the specified session" | |
184 | sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/login | |
185 | sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/sshd | |
186 | ||
c6df5ca4 MW |
187 | if [ -f ${rootfs_path}/etc/pam.d/crond ] |
188 | then | |
189 | sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/crond | |
190 | fi | |
191 | ||
192 | # In addition to disabling pam_loginuid in the above config files | |
193 | # we'll also disable it by linking it to pam_permit to catch any | |
194 | # we missed or any that get installed after the container is built. | |
195 | # | |
196 | # Catch either or both 32 and 64 bit archs. | |
197 | if [ -f ${rootfs_path}/lib/security/pam_loginuid.so ] | |
198 | then | |
199 | ( cd ${rootfs_path}/lib/security/ | |
200 | mv pam_loginuid.so pam_loginuid.so.disabled | |
201 | ln -s pam_permit.so pam_loginuid.so | |
202 | ) | |
203 | fi | |
204 | ||
205 | if [ -f ${rootfs_path}/lib64/security/pam_loginuid.so ] | |
206 | then | |
207 | ( cd ${rootfs_path}/lib64/security/ | |
208 | mv pam_loginuid.so pam_loginuid.so.disabled | |
209 | ln -s pam_permit.so pam_loginuid.so | |
210 | ) | |
211 | fi | |
212 | ||
f5067ecb MW |
213 | # Set default localtime to the host localtime if not set... |
214 | if [ -e /etc/localtime -a ! -e ${rootfs_path}/etc/localtime ] | |
215 | then | |
216 | # if /etc/localtime is a symlink, this should preserve it. | |
217 | cp -a /etc/localtime ${rootfs_path}/etc/localtime | |
218 | fi | |
219 | ||
99c2fb07 MW |
220 | # Deal with some dain bramage in the /etc/init.d/halt script. |
221 | # Trim it and make it our own and link it in before the default | |
222 | # halt script so we can intercept it. This also preventions package | |
223 | # updates from interferring with our interferring with it. | |
224 | # | |
225 | # There's generally not much in the halt script that useful but what's | |
226 | # in there from resetting the hardware clock down is generally very bad. | |
227 | # So we just eliminate the whole bottom half of that script in making | |
228 | # ourselves a copy. That way a major update to the init scripts won't | |
229 | # trash what we've set up. | |
230 | if [ -f ${rootfs_path}/etc/init.d/halt ] | |
231 | then | |
232 | sed -e '/hwclock/,$d' \ | |
233 | < ${rootfs_path}/etc/init.d/halt \ | |
234 | > ${rootfs_path}/etc/init.d/lxc-halt | |
235 | ||
236 | echo '$command -f' >> ${rootfs_path}/etc/init.d/lxc-halt | |
237 | chmod 755 ${rootfs_path}/etc/init.d/lxc-halt | |
238 | ||
239 | # Link them into the rc directories... | |
240 | ( | |
241 | cd ${rootfs_path}/etc/rc.d/rc0.d | |
242 | ln -s ../init.d/lxc-halt S00lxc-halt | |
243 | cd ${rootfs_path}/etc/rc.d/rc6.d | |
244 | ln -s ../init.d/lxc-halt S00lxc-reboot | |
245 | ) | |
246 | fi | |
247 | ||
248 | # configure the network using the dhcp | |
164105f6 MW |
249 | cat <<EOF > ${rootfs_path}/etc/sysconfig/network-scripts/ifcfg-eth0 |
250 | DEVICE=eth0 | |
251 | BOOTPROTO=dhcp | |
252 | ONBOOT=yes | |
54a0b57d | 253 | HOSTNAME=${utsname} |
164105f6 MW |
254 | NM_CONTROLLED=no |
255 | TYPE=Ethernet | |
256 | MTU=${MTU} | |
7e1a946f | 257 | DHCP_HOSTNAME=\`hostname\` |
164105f6 MW |
258 | EOF |
259 | ||
260 | # set the hostname | |
261 | cat <<EOF > ${rootfs_path}/etc/sysconfig/network | |
262 | NETWORKING=yes | |
54a0b57d | 263 | HOSTNAME=${utsname} |
164105f6 MW |
264 | EOF |
265 | ||
266 | # set minimal hosts | |
267 | cat <<EOF > $rootfs_path/etc/hosts | |
268 | 127.0.0.1 localhost $name | |
269 | EOF | |
270 | ||
271 | # set minimal fstab | |
272 | cat <<EOF > $rootfs_path/etc/fstab | |
273 | /dev/root / rootfs defaults 0 0 | |
164105f6 MW |
274 | EOF |
275 | ||
276 | # create lxc compatibility init script | |
277 | if [ "$release" = "6" ]; then | |
278 | cat <<EOF > $rootfs_path/etc/init/lxc-sysinit.conf | |
279 | start on startup | |
280 | env container | |
281 | ||
282 | pre-start script | |
a79df227 | 283 | if [ "x\$container" != "xlxc" -a "x\$container" != "xlibvirt" ]; then |
164105f6 MW |
284 | stop; |
285 | fi | |
a79df227 | 286 | |
164105f6 MW |
287 | rm -f /var/lock/subsys/* |
288 | rm -f /var/run/*.pid | |
a79df227 SG |
289 | [ -e /etc/mtab ] || ln -s /proc/mounts /etc/mtab |
290 | mkdir -p /dev/shm | |
291 | mount -t tmpfs -o nosuid,nodev tmpfs /dev/shm | |
292 | ||
293 | initctl start tty TTY=console | |
164105f6 | 294 | telinit 3 |
a79df227 | 295 | exit 0 |
164105f6 MW |
296 | end script |
297 | EOF | |
298 | elif [ "$release" = "5" ]; then | |
299 | cat <<EOF > $rootfs_path/etc/rc.d/lxc.sysinit | |
300 | #! /bin/bash | |
301 | rm -f /etc/mtab /var/run/*.{pid,lock} /var/lock/subsys/* | |
302 | rm -rf {/,/var}/tmp/* | |
303 | echo "/dev/root / rootfs defaults 0 0" > /etc/mtab | |
304 | exit 0 | |
305 | EOF | |
306 | chmod 755 $rootfs_path/etc/rc.d/lxc.sysinit | |
307 | sed -i 's|si::sysinit:/etc/rc.d/rc.sysinit|si::bootwait:/etc/rc.d/lxc.sysinit|' $rootfs_path/etc/inittab | |
7edae51e CAR |
308 | # prevent mingetty from calling vhangup(2) since it fails with userns. |
309 | # Same issue as oracle template: prevent mingetty from calling vhangup(2) | |
310 | # commit 2e83f7201c5d402478b9849f0a85c62d5b9f1589. | |
311 | sed -i 's|^1:|co:2345:respawn:/sbin/mingetty --nohangup console\n1:|' $rootfs_path/etc/inittab | |
164105f6 MW |
312 | sed -i 's|^\([56]:\)|#\1|' $rootfs_path/etc/inittab |
313 | fi | |
314 | ||
315 | dev_path="${rootfs_path}/dev" | |
316 | rm -rf $dev_path | |
317 | mkdir -p $dev_path | |
318 | mknod -m 666 ${dev_path}/null c 1 3 | |
319 | mknod -m 666 ${dev_path}/zero c 1 5 | |
320 | mknod -m 666 ${dev_path}/random c 1 8 | |
321 | mknod -m 666 ${dev_path}/urandom c 1 9 | |
322 | mkdir -m 755 ${dev_path}/pts | |
323 | mkdir -m 1777 ${dev_path}/shm | |
324 | mknod -m 666 ${dev_path}/tty c 5 0 | |
325 | mknod -m 666 ${dev_path}/tty0 c 4 0 | |
326 | mknod -m 666 ${dev_path}/tty1 c 4 1 | |
327 | mknod -m 666 ${dev_path}/tty2 c 4 2 | |
328 | mknod -m 666 ${dev_path}/tty3 c 4 3 | |
329 | mknod -m 666 ${dev_path}/tty4 c 4 4 | |
330 | mknod -m 600 ${dev_path}/console c 5 1 | |
331 | mknod -m 666 ${dev_path}/full c 1 7 | |
332 | mknod -m 600 ${dev_path}/initctl p | |
333 | mknod -m 666 ${dev_path}/ptmx c 5 2 | |
334 | ||
1ecee40b MW |
335 | # setup console and tty[1-4] for login. note that /dev/console and |
336 | # /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and | |
337 | # /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks. | |
338 | # lxc will maintain these links and bind mount ptys over /dev/lxc/* | |
42e53c29 | 339 | # since lxc.tty.dir is specified in the config. |
1ecee40b MW |
340 | |
341 | # allow root login on console, tty[1-4], and pts/0 for libvirt | |
342 | echo "# LXC (Linux Containers)" >>${rootfs_path}/etc/securetty | |
343 | echo "lxc/console" >>${rootfs_path}/etc/securetty | |
344 | echo "lxc/tty1" >>${rootfs_path}/etc/securetty | |
345 | echo "lxc/tty2" >>${rootfs_path}/etc/securetty | |
346 | echo "lxc/tty3" >>${rootfs_path}/etc/securetty | |
347 | echo "lxc/tty4" >>${rootfs_path}/etc/securetty | |
348 | echo "# For libvirt/Virtual Machine Monitor" >>${rootfs_path}/etc/securetty | |
349 | echo "pts/0" >>${rootfs_path}/etc/securetty | |
350 | ||
7edae51e CAR |
351 | # prevent mingetty from calling vhangup(2) since it fails with userns. |
352 | # Same issue as oracle template: prevent mingetty from calling vhangup(2) | |
353 | # commit 2e83f7201c5d402478b9849f0a85c62d5b9f1589. | |
3335c608 | 354 | sed -i 's|mingetty|mingetty --nohangup|' $rootfs_path/etc/init/tty.conf |
7edae51e | 355 | |
b4f7af7a MW |
356 | if [ ${root_display_password} = "yes" ] |
357 | then | |
358 | echo "Setting root password to '$root_password'" | |
359 | fi | |
360 | if [ ${root_store_password} = "yes" ] | |
361 | then | |
362 | touch ${config_path}/tmp_root_pass | |
363 | chmod 600 ${config_path}/tmp_root_pass | |
364 | echo ${root_password} > ${config_path}/tmp_root_pass | |
365 | echo "Storing root password in '${config_path}/tmp_root_pass'" | |
366 | fi | |
367 | ||
164105f6 | 368 | echo "root:$root_password" | chroot $rootfs_path chpasswd |
826cde7c MW |
369 | |
370 | if [ ${root_expire_password} = "yes" ] | |
371 | then | |
372 | # Also set this password as expired to force the user to change it! | |
373 | chroot $rootfs_path passwd -e root | |
374 | fi | |
164105f6 MW |
375 | |
376 | # This will need to be enhanced for CentOS 7 when systemd | |
377 | # comes into play... /\/\|=mhw=|\/\/ | |
378 | ||
379 | return 0 | |
380 | } | |
381 | ||
382 | configure_centos_init() | |
383 | { | |
384 | sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.sysinit | |
385 | sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.d/rc.sysinit | |
386 | if [ "$release" = "6" ]; then | |
387 | chroot ${rootfs_path} chkconfig udev-post off | |
388 | fi | |
389 | chroot ${rootfs_path} chkconfig network on | |
779b47fd MW |
390 | |
391 | if [ -d ${rootfs_path}/etc/init ] | |
392 | then | |
393 | # This is to make upstart honor SIGPWR | |
394 | cat <<EOF >${rootfs_path}/etc/init/power-status-changed.conf | |
395 | # power-status-changed - shutdown on SIGPWR | |
396 | # | |
397 | start on power-status-changed | |
0395c47c | 398 | |
779b47fd MW |
399 | exec /sbin/shutdown -h now "SIGPWR received" |
400 | EOF | |
401 | fi | |
164105f6 MW |
402 | } |
403 | ||
404 | download_centos() | |
405 | { | |
406 | ||
491a01cf | 407 | # check the mini CentOS was not already downloaded |
164105f6 MW |
408 | INSTALL_ROOT=$cache/partial |
409 | mkdir -p $INSTALL_ROOT | |
410 | if [ $? -ne 0 ]; then | |
411 | echo "Failed to create '$INSTALL_ROOT' directory" | |
412 | return 1 | |
413 | fi | |
414 | ||
491a01cf RM |
415 | # download a mini CentOS into a cache |
416 | echo "Downloading CentOS minimal ..." | |
75d87a4b DS |
417 | YUM0="yum --installroot $INSTALL_ROOT -y --nogpgcheck" |
418 | ||
85ccd3d5 | 419 | if yum -h | grep -q 'releasever=RELEASEVER'; then |
75d87a4b | 420 | YUM="$YUM0 --releasever=$release" |
87a92d42 | 421 | else |
75d87a4b | 422 | YUM="$YUM0" |
6d41f7df | 423 | fi |
c227acde | 424 | PKG_LIST="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils cronie" |
164105f6 MW |
425 | |
426 | # use temporary repository definition | |
e54bd54f EG |
427 | # always prefer the repo given by the user |
428 | # if no repo given, use mirrorlist.centos.org for i386 and x86_64 | |
429 | # and http://mirror.centos.org/altarch/ otherwise | |
164105f6 MW |
430 | REPO_FILE=$INSTALL_ROOT/etc/yum.repos.d/lxc-centos-temp.repo |
431 | mkdir -p $(dirname $REPO_FILE) | |
3a6ef65a HD |
432 | if [ -n "$repo" ]; then |
433 | cat <<EOF > $REPO_FILE | |
434 | [base] | |
435 | name=local repository | |
436 | baseurl="$repo" | |
437 | EOF | |
e54bd54f | 438 | elif [ ${basearch} = 'i386' ] || [ ${basearch} = 'x86_64' ]; then |
3a6ef65a | 439 | cat <<EOF > $REPO_FILE |
164105f6 MW |
440 | [base] |
441 | name=CentOS-$release - Base | |
08754f30 | 442 | mirrorlist=http://mirrorlist.centos.org/?release=$release&arch=$basearch&repo=os |
164105f6 MW |
443 | |
444 | [updates] | |
445 | name=CentOS-$release - Updates | |
08754f30 | 446 | mirrorlist=http://mirrorlist.centos.org/?release=$release&arch=$basearch&repo=updates |
e54bd54f EG |
447 | EOF |
448 | else | |
449 | cat <<EOF > $REPO_FILE | |
450 | [base] | |
451 | name=CentOS-$release - Base | |
452 | baseurl=http://mirror.centos.org/altarch/7/os/$basearch/ | |
453 | ||
454 | [updates] | |
455 | name=CentOS-$release - Updates | |
456 | baseurl=http://mirror.centos.org/altarch/7/updates/$basearch/ | |
164105f6 | 457 | EOF |
3a6ef65a | 458 | fi |
164105f6 MW |
459 | |
460 | # create minimal device nodes, needed for "yum install" and "yum update" process | |
461 | mkdir -p $INSTALL_ROOT/dev | |
462 | force_mknod 666 $INSTALL_ROOT/dev/null c 1 3 | |
463 | force_mknod 666 $INSTALL_ROOT/dev/urandom c 1 9 | |
464 | ||
465 | $YUM install $PKG_LIST | |
466 | ||
1c5a3c58 | 467 | # create symlink for /var/run -> ../run |
468 | if [ "$release" = "7" ]; then | |
469 | mv $INSTALL_ROOT/var/run/* $INSTALL_ROOT/run/ | |
470 | rmdir $INSTALL_ROOT/var/run | |
471 | ln -sf ../run $INSTALL_ROOT/var/run | |
472 | fi | |
473 | ||
164105f6 MW |
474 | if [ $? -ne 0 ]; then |
475 | echo "Failed to download the rootfs, aborting." | |
476 | return 1 | |
477 | fi | |
478 | ||
479 | # use same nameservers as hosts, needed for "yum update later" | |
480 | cp /etc/resolv.conf $INSTALL_ROOT/etc/ | |
481 | ||
482 | # check whether rpmdb is under $HOME | |
483 | if [ ! -e $INSTALL_ROOT/var/lib/rpm/Packages -a -e $INSTALL_ROOT/$HOME/.rpmdb/Packages ]; then | |
484 | echo "Fixing rpmdb location ..." | |
485 | mv $INSTALL_ROOT/$HOME/.rpmdb/[A-Z]* $INSTALL_ROOT/var/lib/rpm/ | |
486 | rm -rf $INSTALL_ROOT/$HOME/.rpmdb | |
487 | chroot $INSTALL_ROOT rpm --rebuilddb 2>/dev/null | |
488 | fi | |
489 | ||
490 | # check whether rpmdb version is correct | |
491 | chroot $INSTALL_ROOT rpm --quiet -q yum 2>/dev/null | |
492 | ret=$? | |
493 | ||
494 | # if "rpm -q" doesn't work due to rpmdb version difference, | |
495 | # then we need to redo the process using the newly-installed yum | |
496 | if [ $ret -gt 0 ]; then | |
497 | echo "Reinstalling packages ..." | |
498 | mv $REPO_FILE $REPO_FILE.tmp | |
499 | mkdir $INSTALL_ROOT/etc/yum.repos.disabled | |
500 | mv $INSTALL_ROOT/etc/yum.repos.d/*.repo $INSTALL_ROOT/etc/yum.repos.disabled/ | |
501 | mv $REPO_FILE.tmp $REPO_FILE | |
502 | mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/etc | |
503 | cp /etc/resolv.conf $INSTALL_ROOT/$INSTALL_ROOT/etc/ | |
504 | mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/dev | |
505 | mknod -m 666 $INSTALL_ROOT/$INSTALL_ROOT/dev/null c 1 3 | |
506 | mknod -m 666 $INSTALL_ROOT/$INSTALL_ROOT/dev/urandom c 1 9 | |
507 | mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/var/cache/yum | |
508 | cp -al $INSTALL_ROOT/var/cache/yum/* $INSTALL_ROOT/$INSTALL_ROOT/var/cache/yum/ | |
75d87a4b | 509 | chroot $INSTALL_ROOT $YUM0 install $PKG_LIST |
164105f6 MW |
510 | if [ $? -ne 0 ]; then |
511 | echo "Failed to download the rootfs, aborting." | |
512 | return 1 | |
513 | fi | |
514 | mv $INSTALL_ROOT/$INSTALL_ROOT $INSTALL_ROOT.tmp | |
515 | rm -rf $INSTALL_ROOT | |
516 | mv $INSTALL_ROOT.tmp $INSTALL_ROOT | |
517 | fi | |
518 | ||
519 | rm -f $REPO_FILE | |
520 | rm -rf $INSTALL_ROOT/var/cache/yum/* | |
521 | ||
522 | mv "$INSTALL_ROOT" "$cache/rootfs" | |
523 | echo "Download complete." | |
524 | ||
525 | return 0 | |
526 | } | |
527 | ||
528 | copy_centos() | |
529 | { | |
530 | ||
491a01cf | 531 | # make a local copy of the mini CentOS |
164105f6 MW |
532 | echo -n "Copying rootfs to $rootfs_path ..." |
533 | #cp -a $cache/rootfs-$arch $rootfs_path || return 1 | |
534 | # i prefer rsync (no reason really) | |
535 | mkdir -p $rootfs_path | |
6273aef1 | 536 | rsync -SHaAX $cache/rootfs/ $rootfs_path/ |
b4f7af7a | 537 | echo |
164105f6 MW |
538 | return 0 |
539 | } | |
540 | ||
541 | update_centos() | |
542 | { | |
543 | YUM="chroot $cache/rootfs yum -y --nogpgcheck" | |
544 | $YUM update | |
545 | if [ $? -ne 0 ]; then | |
546 | return 1 | |
547 | fi | |
548 | $YUM clean packages | |
549 | } | |
550 | ||
551 | install_centos() | |
552 | { | |
553 | mkdir -p /var/lock/subsys/ | |
554 | ( | |
17abf278 | 555 | flock -x 9 |
164105f6 MW |
556 | if [ $? -ne 0 ]; then |
557 | echo "Cache repository is busy." | |
558 | return 1 | |
559 | fi | |
560 | ||
561 | echo "Checking cache download in $cache/rootfs ... " | |
562 | if [ ! -e "$cache/rootfs" ]; then | |
563 | download_centos | |
564 | if [ $? -ne 0 ]; then | |
491a01cf | 565 | echo "Failed to download 'CentOS base'" |
164105f6 MW |
566 | return 1 |
567 | fi | |
568 | else | |
569 | echo "Cache found. Updating..." | |
570 | update_centos | |
571 | if [ $? -ne 0 ]; then | |
491a01cf | 572 | echo "Failed to update 'CentOS base', continuing with last known good cache" |
164105f6 MW |
573 | else |
574 | echo "Update finished" | |
575 | fi | |
576 | fi | |
577 | ||
578 | echo "Copy $cache/rootfs to $rootfs_path ... " | |
579 | copy_centos | |
580 | if [ $? -ne 0 ]; then | |
581 | echo "Failed to copy rootfs" | |
582 | return 1 | |
583 | fi | |
584 | ||
585 | return 0 | |
586 | ||
17abf278 | 587 | ) 9>/var/lock/subsys/lxc-centos |
164105f6 MW |
588 | |
589 | return $? | |
590 | } | |
591 | ||
b4f7af7a | 592 | create_hwaddr() |
164105f6 | 593 | { |
08754f30 | 594 | openssl rand -hex 5 | sed -e 's/\(..\)/:\1/g; s/^/fe/' |
b4f7af7a | 595 | } |
164105f6 | 596 | |
b4f7af7a MW |
597 | copy_configuration() |
598 | { | |
164105f6 | 599 | mkdir -p $config_path |
b4f7af7a | 600 | |
7a96a068 CB |
601 | grep -q "^lxc.rootfs.path" $config_path/config 2>/dev/null || echo " |
602 | lxc.rootfs.path = $rootfs_path | |
b4f7af7a MW |
603 | " >> $config_path/config |
604 | ||
605 | # The following code is to create static MAC addresses for each | |
606 | # interface in the container. This code will work for multiple | |
607 | # interfaces in the default config. | |
608 | mv $config_path/config $config_path/config.def | |
609 | while read LINE | |
610 | do | |
611 | # This should catch variable expansions from the default config... | |
612 | if expr "${LINE}" : '.*\$' > /dev/null 2>&1 | |
613 | then | |
614 | LINE=$(eval "echo \"${LINE}\"") | |
615 | fi | |
616 | ||
617 | # There is a tab and a space in the regex bracket below! | |
618 | # Seems that \s doesn't work in brackets. | |
619 | KEY=$(expr "${LINE}" : '\s*\([^ ]*\)\s*=') | |
620 | ||
7fa3f2e9 | 621 | if [[ "${KEY}" != "lxc.net.0.hwaddr" ]] |
b4f7af7a MW |
622 | then |
623 | echo ${LINE} >> $config_path/config | |
624 | ||
7fa3f2e9 | 625 | if [[ "${KEY}" == "lxc.net.0.link" ]] |
b4f7af7a | 626 | then |
7fa3f2e9 | 627 | echo "lxc.net.0.hwaddr = $(create_hwaddr)" >> $config_path/config |
b4f7af7a MW |
628 | fi |
629 | fi | |
630 | done < $config_path/config.def | |
631 | ||
632 | rm -f $config_path/config.def | |
633 | ||
1ecee40b MW |
634 | if [ -e "@LXCTEMPLATECONFIG@/centos.common.conf" ]; then |
635 | echo " | |
636 | # Include common configuration | |
637 | lxc.include = @LXCTEMPLATECONFIG@/centos.common.conf | |
638 | " >> $config_path/config | |
639 | fi | |
640 | ||
641 | # Append things which require expansion here... | |
164105f6 | 642 | cat <<EOF >> $config_path/config |
e13923c7 | 643 | lxc.arch = $arch |
b67771bc | 644 | lxc.uts.name = $utsname |
164105f6 | 645 | |
b4f7af7a | 646 | # When using LXC with apparmor, uncomment the next line to run unconfined: |
a1d5fdfd | 647 | #lxc.apparmor.profile = unconfined |
b4f7af7a | 648 | |
164105f6 | 649 | # example simple networking setup, uncomment to enable |
7fa3f2e9 | 650 | #lxc.net.0.type = $lxc_network_type |
651 | #lxc.net.0.flags = up | |
652 | #lxc.net.0.link = $lxc_network_link | |
653 | #lxc.net.0.name = eth0 | |
b4f7af7a MW |
654 | # Additional example for veth network type |
655 | # static MAC address, | |
7fa3f2e9 | 656 | #lxc.net.0.hwaddr = 00:16:3e:77:52:20 |
b4f7af7a MW |
657 | # persistent veth device name on host side |
658 | # Note: This may potentially collide with other containers of same name! | |
7fa3f2e9 | 659 | #lxc.net.0.veth.pair = v-$name-e0 |
164105f6 | 660 | |
164105f6 MW |
661 | EOF |
662 | ||
663 | if [ $? -ne 0 ]; then | |
b4f7af7a MW |
664 | echo "Failed to add configuration" |
665 | return 1 | |
164105f6 MW |
666 | fi |
667 | ||
668 | return 0 | |
669 | } | |
670 | ||
671 | clean() | |
672 | { | |
673 | ||
674 | if [ ! -e $cache ]; then | |
b4f7af7a | 675 | exit 0 |
164105f6 MW |
676 | fi |
677 | ||
678 | # lock, so we won't purge while someone is creating a repository | |
679 | ( | |
b4f7af7a MW |
680 | flock -x 9 |
681 | if [ $? != 0 ]; then | |
682 | echo "Cache repository is busy." | |
683 | exit 1 | |
684 | fi | |
164105f6 | 685 | |
491a01cf | 686 | echo -n "Purging the download cache for CentOS-$release..." |
b4f7af7a MW |
687 | rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 |
688 | exit 0 | |
164105f6 | 689 | |
b4f7af7a | 690 | ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-centos |
164105f6 MW |
691 | } |
692 | ||
693 | usage() | |
694 | { | |
695 | cat <<EOF | |
696 | usage: | |
697 | $1 -n|--name=<container_name> | |
6bfb727c | 698 | [-p|--path=<path>] [-c|--clean] [-R|--release=<CentOS_release>] [-a|--arch=<arch of the container>] |
164105f6 MW |
699 | [-h|--help] |
700 | Mandatory args: | |
701 | -n,--name container name, used to as an identifier for that container from now on | |
702 | Optional args: | |
08754f30 | 703 | -p,--path path to where the container rootfs will be created, defaults to /var/lib/lxc/name. |
164105f6 | 704 | -c,--clean clean the cache |
491a01cf | 705 | -R,--release CentOS release for the new container. If the host is CentOS, then it will default to the host's release. |
164105f6 | 706 | --fqdn fully qualified domain name (FQDN) for DNS and system naming |
3a6ef65a | 707 | --repo repository to use (url) |
08754f30 | 708 | -a,--arch Define what arch the container will be [i686,x86_64] |
164105f6 MW |
709 | -h,--help print this help |
710 | EOF | |
711 | return 0 | |
712 | } | |
713 | ||
3a6ef65a | 714 | options=$(getopt -o a:hp:n:cR: -l help,path:,rootfs:,name:,clean,release:,repo:,arch:,fqdn: -- "$@") |
164105f6 MW |
715 | if [ $? -ne 0 ]; then |
716 | usage $(basename $0) | |
717 | exit 1 | |
718 | fi | |
164105f6 | 719 | |
4849ab99 | 720 | arch=$(uname -m) |
08754f30 | 721 | eval set -- "$options" |
164105f6 MW |
722 | while true |
723 | do | |
724 | case "$1" in | |
725 | -h|--help) usage $0 && exit 0;; | |
726 | -p|--path) path=$2; shift 2;; | |
9f177a00 | 727 | --rootfs) rootfs_path=$2; shift 2;; |
164105f6 | 728 | -n|--name) name=$2; shift 2;; |
6976826f | 729 | -c|--clean) clean=1; shift 1;; |
164105f6 | 730 | -R|--release) release=$2; shift 2;; |
2ae8252a | 731 | --repo) repo="$2"; shift 2;; |
08754f30 | 732 | -a|--arch) newarch=$2; shift 2;; |
164105f6 MW |
733 | --fqdn) utsname=$2; shift 2;; |
734 | --) shift 1; break ;; | |
735 | *) break ;; | |
736 | esac | |
737 | done | |
738 | ||
739 | if [ ! -z "$clean" -a -z "$path" ]; then | |
740 | clean || exit 1 | |
741 | exit 0 | |
742 | fi | |
743 | ||
08754f30 MW |
744 | basearch=${arch} |
745 | # Map a few architectures to their generic CentOS repository archs. | |
746 | # The two ARM archs are a bit of a guesstimate for the v5 and v6 | |
747 | # archs. V6 should have hardware floating point (Rasberry Pi). | |
748 | # The "arm" arch is safer (no hardware floating point). So | |
749 | # there may be cases where we "get it wrong" for some v6 other | |
750 | # than RPi. | |
751 | case "$arch" in | |
752 | i686) basearch=i386 ;; | |
753 | armv3l|armv4l|armv5l) basearch=arm ;; | |
754 | armv6l|armv7l|armv8l) basearch=armhfp ;; | |
755 | *) ;; | |
756 | esac | |
757 | ||
758 | # Somebody wants to specify an arch. This is very limited case. | |
759 | # i386/i586/i686 on i386/x86_64 | |
760 | # - or - | |
761 | # x86_64 on x86_64 | |
762 | if [ "${newarch}" != "" -a "${newarch}" != "${arch}" ] | |
763 | then | |
764 | case "${newarch}" in | |
765 | i386|i586|i686) | |
766 | if [ "${basearch}" = "i386" -o "${basearch}" = "x86_64" ] | |
767 | then | |
768 | # Make the arch a generic x86 32 bit... | |
769 | arch=${newarch} | |
770 | basearch=i386 | |
771 | else | |
772 | basearch=bad | |
773 | fi | |
774 | ;; | |
775 | *) | |
776 | basearch=bad | |
777 | ;; | |
778 | esac | |
779 | ||
780 | if [ "${basearch}" = "bad" ] | |
781 | then | |
782 | echo "You cannot build a ${newarch} CentOS container on a ${arch} host. Sorry!" | |
783 | exit 1 | |
784 | fi | |
785 | fi | |
786 | ||
6dc6f80b KC |
787 | # Allow the cache base to be set by environment variable |
788 | cache_base=${LXC_CACHE_PATH:-"@LOCALSTATEDIR@/cache/lxc"}/centos/$basearch | |
08754f30 | 789 | |
b4f7af7a MW |
790 | # Let's do something better for the initial root password. |
791 | # It's not perfect but it will defeat common scanning brute force | |
792 | # attacks in the case where ssh is exposed. It will also be set to | |
793 | # expired, forcing the user to change it at first login. | |
794 | if [ "${root_password}" = "" ] | |
795 | then | |
796 | root_password=Root-${name}-${RANDOM} | |
797 | else | |
798 | # If it's got a ding in it, try and expand it! | |
799 | if [ $(expr "${root_password}" : '.*$.') != 0 ] | |
800 | then | |
801 | root_password=$(eval echo "${root_password}") | |
802 | fi | |
803 | ||
ec64264d | 804 | # If it has more than 3 consecutive X's in it, feed it |
b4f7af7a MW |
805 | # through mktemp as a template. |
806 | if [ $(expr "${root_password}" : '.*XXXX') != 0 ] | |
807 | then | |
808 | root_password=$(mktemp -u ${root_password}) | |
809 | fi | |
810 | fi | |
811 | ||
164105f6 MW |
812 | if [ -z "${utsname}" ]; then |
813 | utsname=${name} | |
814 | fi | |
815 | ||
816 | # This follows a standard "resolver" convention that an FQDN must have | |
817 | # at least two dots or it is considered a local relative host name. | |
818 | # If it doesn't, append the dns domain name of the host system. | |
819 | # | |
820 | # This changes one significant behavior when running | |
821 | # "lxc_create -n Container_Name" without using the | |
822 | # --fqdn option. | |
823 | # | |
824 | # Old behavior: | |
825 | # utsname and hostname = Container_Name | |
826 | # New behavior: | |
827 | # utsname and hostname = Container_Name.Domain_Name | |
828 | ||
829 | if [ $(expr "$utsname" : '.*\..*\.') = 0 ]; then | |
b4f7af7a | 830 | if [[ "$(dnsdomainname)" != "" && "$(dnsdomainname)" != "localdomain" ]]; then |
164105f6 MW |
831 | utsname=${utsname}.$(dnsdomainname) |
832 | fi | |
833 | fi | |
834 | ||
835 | type yum >/dev/null 2>&1 | |
836 | if [ $? -ne 0 ]; then | |
837 | echo "'yum' command is missing" | |
838 | exit 1 | |
839 | fi | |
840 | ||
841 | if [ -z "$path" ]; then | |
842 | path=$default_path/$name | |
843 | fi | |
844 | ||
845 | if [ -z "$release" ]; then | |
846 | if [ "$is_centos" -a "$centos_host_ver" ]; then | |
847 | release=$centos_host_ver | |
c6df5ca4 MW |
848 | elif [ "$is_redhat" -a "$redhat_host_ver" ]; then |
849 | # This is needed to clean out bullshit like 6workstation and 6server. | |
850 | release=$(expr $redhat_host_ver : '\([0-9.]*\)') | |
164105f6 | 851 | else |
420382d5 EG |
852 | echo "This is not a CentOS or Red Hat host and release is missing, defaulting to 7, use -R|--release to specify release" |
853 | release=7 | |
164105f6 MW |
854 | fi |
855 | fi | |
856 | ||
164105f6 MW |
857 | if [ "$(id -u)" != "0" ]; then |
858 | echo "This script should be run as 'root'" | |
859 | exit 1 | |
860 | fi | |
861 | ||
164105f6 MW |
862 | if [ -z "$rootfs_path" ]; then |
863 | rootfs_path=$path/rootfs | |
7a96a068 CB |
864 | # check for 'lxc.rootfs.path' passed in through default config by lxc-create |
865 | if grep -q '^lxc.rootfs.path' $path/config 2>/dev/null ; then | |
866 | rootfs_path=$(sed -e '/^lxc.rootfs.path\s*=/!d' -e 's/\s*#.*//' \ | |
867 | -e 's/^lxc.rootfs.path\s*=\s*//' -e q $path/config) | |
164105f6 MW |
868 | fi |
869 | fi | |
08754f30 | 870 | config_path=$path |
164105f6 MW |
871 | cache=$cache_base/$release |
872 | ||
873 | revert() | |
874 | { | |
875 | echo "Interrupted, so cleaning up" | |
876 | lxc-destroy -n $name | |
877 | # maybe was interrupted before copy config | |
878 | rm -rf $path | |
164105f6 MW |
879 | echo "exiting..." |
880 | exit 1 | |
881 | } | |
882 | ||
883 | trap revert SIGHUP SIGINT SIGTERM | |
884 | ||
885 | copy_configuration | |
886 | if [ $? -ne 0 ]; then | |
887 | echo "failed write configuration file" | |
888 | exit 1 | |
889 | fi | |
890 | ||
891 | install_centos | |
892 | if [ $? -ne 0 ]; then | |
491a01cf | 893 | echo "failed to install CentOS" |
164105f6 MW |
894 | exit 1 |
895 | fi | |
896 | ||
897 | configure_centos | |
898 | if [ $? -ne 0 ]; then | |
491a01cf | 899 | echo "failed to configure CentOS for a container" |
164105f6 MW |
900 | exit 1 |
901 | fi | |
902 | ||
903 | configure_centos_init | |
904 | ||
a2780518 | 905 | if [ ! -z "$clean" ]; then |
164105f6 MW |
906 | clean || exit 1 |
907 | exit 0 | |
908 | fi | |
b4f7af7a MW |
909 | echo " |
910 | Container rootfs and config have been created. | |
911 | Edit the config file to check/enable networking setup. | |
912 | " | |
913 | ||
914 | if [ ${root_display_password} = "yes" ] | |
915 | then | |
916 | echo "The temporary password for root is: '$root_password' | |
917 | ||
918 | You may want to note that password down before starting the container. | |
919 | " | |
920 | fi | |
921 | ||
922 | if [ ${root_store_password} = "yes" ] | |
923 | then | |
924 | echo "The temporary root password is stored in: | |
925 | ||
926 | '${config_path}/tmp_root_pass' | |
927 | " | |
928 | fi | |
929 | ||
930 | if [ ${root_prompt_password} = "yes" ] | |
931 | then | |
932 | echo "Invoking the passwd command in the container to set the root password. | |
933 | ||
934 | chroot ${rootfs_path} passwd | |
935 | " | |
936 | chroot ${rootfs_path} passwd | |
937 | else | |
826cde7c MW |
938 | if [ ${root_expire_password} = "yes" ] |
939 | then | |
d510d522 NW |
940 | if ( mountpoint -q -- "${rootfs_path}" ) |
941 | then | |
942 | echo "To reset the root password, you can do: | |
943 | ||
944 | lxc-start -n ${name} | |
945 | lxc-attach -n ${name} -- passwd | |
946 | lxc-stop -n ${name} | |
947 | " | |
948 | else | |
949 | echo " | |
b4f7af7a MW |
950 | The root password is set up as "expired" and will require it to be changed |
951 | at first login, which you should do as soon as possible. If you lose the | |
952 | root password or wish to change it without starting the container, you | |
953 | can change it from the host by running the following command (which will | |
954 | also reset the expired flag): | |
955 | ||
956 | chroot ${rootfs_path} passwd | |
957 | " | |
d510d522 | 958 | fi |
826cde7c | 959 | fi |
b4f7af7a | 960 | fi |