Created
April 21, 2015 11:03
-
-
Save netmarkjp/1d8287a2b647e65c6b2e to your computer and use it in GitHub Desktop.
lxc-centos7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from fabric.api import run, env | |
# fab -H <CONTAINER_IP> initialize | |
env.user = 'root' | |
def initialize(): | |
run('yes | cp -rf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime') | |
run('install -d -o root -g root -m 700 /root/.ssh') | |
run('echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC/0VbBYFXrvs6xM/5hj+SCb6Vg96aceKdlg//SA4j6oeZ8jTX1ySFwqYIZtRvyVANDgHXfbyFmgpCeuF2Fjjy/+pD31NkeAP2m0QyYm/UWZf34wrxcDJOdUzRe+lNFDL7tYUUM9tJG6u4Qo8nSJupK+seXdbRmOzCCjp3wczmA9ko95ep7IRfvyqBPla22RAJfzmwhkM2XdkEBvMQClH+tISKnxkpS684q8PhW5uGe8nCQDawb0ZXS8H43hQcPDsgIW3DGMVSgFxWDBiMd0B/ro47FKCBnwR+ygaWWc42azD4zcV7/Rn0epKnNqyihfwg/kLpOt6+1lpSMYMX4u8Lp baba@apps" | tee -a /root/.ssh/authorized_keys') | |
run('chmod 600 /root/.ssh/authorized_keys') | |
run('yum -y install bash-completion vim-enhanced strace dstat') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# template script for generating centos container for LXC | |
# | |
# lxc: linux Container library | |
# Authors: | |
# Daniel Lezcano <[email protected]> | |
# Ramez Hanna <[email protected]> | |
# Fajar A. Nugraha <[email protected]> | |
# Michael H. Warfield <[email protected]> | |
# This library is free software; you can redistribute it and/or | |
# modify it under the terms of the GNU Lesser General Public | |
# License as published by the Free Software Foundation; either | |
# version 2.1 of the License, or (at your option) any later version. | |
# This library is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
# Lesser General Public License for more details. | |
# You should have received a copy of the GNU Lesser General Public | |
# License along with this library; if not, write to the Free Software | |
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
#Configurations | |
default_path=/var/lib/lxc | |
# Some combinations of the tuning knobs below do not exactly make sense. | |
# but that's ok. | |
# | |
# If the "root_password" is non-blank, use it, else set a default. | |
# This can be passed to the script as an environment variable and is | |
# set by a shell conditional assignment. Looks weird but it is what it is. | |
# | |
# If the root password contains a ding ($) then try to expand it. | |
# That will pick up things like ${name} and ${RANDOM}. | |
# If the root password contains more than 3 consecutive X's, pass it as | |
# a template to mktemp and take the result. | |
# | |
# If root_display_password = yes, display the temporary root password at exit. | |
# If root_store_password = yes, store it in the configuration directory | |
# If root_prompt_password = yes, invoke "passwd" to force the user to change | |
# the root password after the container is created. | |
# If root_expire_password = yes, you will be prompted to change the root | |
# password at the first login. | |
# | |
# These are conditional assignments... The can be overridden from the | |
# preexisting environment variables... | |
# | |
# Make sure this is in single quotes to defer expansion to later! | |
# :{root_password='Root-${name}-${RANDOM}'} | |
: ${root_password='p@ssw0rd'} | |
# Now, it doesn't make much sense to display, store, and force change | |
# together. But, we gotta test, right??? | |
: ${root_display_password='no'} | |
: ${root_store_password='yes'} | |
# Prompting for something interactive has potential for mayhem | |
# with users running under the API... Don't default to "yes" | |
: ${root_prompt_password='no'} | |
# Expire root password? Default to yes, but can be overridden from | |
# the environment variable | |
: ${root_expire_password='no'} | |
# These are only going into comments in the resulting config... | |
lxc_network_type=veth | |
lxc_network_link=lxcbr0 | |
# is this centos? | |
# Alow for weird remixes like the Raspberry Pi | |
# | |
# Use the Mitre standard CPE identifier for the release ID if possible... | |
# This may be in /etc/os-release or /etc/system-release-cpe. We | |
# should be able to use EITHER. Give preference to /etc/os-release for now. | |
# Detect use under userns (unsupported) | |
for arg in "$@"; do | |
[ "$arg" = "--" ] && break | |
if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then | |
echo "This template can't be used for unprivileged containers." 1>&2 | |
echo "You may want to try the \"download\" template instead." 1>&2 | |
exit 1 | |
fi | |
done | |
# Make sure the usual locations are in PATH | |
export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin | |
if [ -e /etc/os-release ] | |
then | |
# This is a shell friendly configuration file. We can just source it. | |
# What we're looking for in here is the ID, VERSION_ID and the CPE_NAME | |
. /etc/os-release | |
echo "Host CPE ID from /etc/os-release: ${CPE_NAME}" | |
fi | |
if [ "${CPE_NAME}" = "" -a -e /etc/system-release-cpe ] | |
then | |
CPE_NAME=$(head -n1 /etc/system-release-cpe) | |
CPE_URI=$(expr ${CPE_NAME} : '\([^:]*:[^:]*\)') | |
if [ "${CPE_URI}" != "cpe:/o" ] | |
then | |
CPE_NAME= | |
else | |
# Probably a better way to do this but sill remain posix | |
# compatible but this works, shrug... | |
# Must be nice and not introduce convenient bashisms here. | |
# | |
# According to the official registration at Mitre and NIST, | |
# this should have been something like this for CentOS: | |
# cpe:/o:centos:centos:6 | |
# or this: | |
# cpe:/o:centos:centos:6.5 | |
# | |
ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:\([^:]*\)') | |
# The "enterprise_linux" is a bone toss back to RHEL. | |
# Since CentOS and RHEL are so tightly coupled, we'll | |
# take the RHEL version if we're running on it and do the | |
# equivalent version for CentOS. | |
if [ ${ID} = "linux" -o ${ID} = "enterprise_linux" ] | |
then | |
# Instead we got this: cpe:/o:centos:linux:6 | |
ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:\([^:]*\)') | |
fi | |
VERSION_ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\)') | |
echo "Host CPE ID from /etc/system-release-cpe: ${CPE_NAME}" | |
fi | |
fi | |
if [ "${CPE_NAME}" != "" -a "${ID}" = "centos" -a "${VERSION_ID}" != "" ] | |
then | |
centos_host_ver=${VERSION_ID} | |
is_centos=true | |
elif [ "${CPE_NAME}" != "" -a "${ID}" = "redhat" -a "${VERSION_ID}" != "" ] | |
then | |
redhat_host_ver=${VERSION_ID} | |
is_redhat=true | |
elif [ -e /etc/centos-release ] | |
then | |
# Only if all other methods fail, try to parse the redhat-release file. | |
centos_host_ver=$( sed -e '/^CentOS /!d' -e 's/CentOS.*\srelease\s*\([0-9][0-9.]*\)\s.*/\1/' < /etc/centos-release ) | |
if [ "$centos_host_ver" != "" ] | |
then | |
is_centos=true | |
fi | |
fi | |
force_mknod() | |
{ | |
# delete a device node if exists, and create a new one | |
rm -f $2 && mknod -m $1 $2 $3 $4 $5 | |
} | |
configure_centos() | |
{ | |
# disable selinux in centos | |
mkdir -p $rootfs_path/selinux | |
echo 0 > $rootfs_path/selinux/enforce | |
# Also kill it in the /etc/selinux/config file if it's there... | |
if [ -f $rootfs_path/etc/selinux/config ] | |
then | |
sed -i '/^SELINUX=/s/.*/SELINUX=disabled/' $rootfs_path/etc/selinux/config | |
fi | |
# Nice catch from Dwight Engen in the Oracle template. | |
# Wantonly plagerized here with much appreciation. | |
if [ -f $rootfs_path/usr/sbin/selinuxenabled ]; then | |
mv $rootfs_path/usr/sbin/selinuxenabled $rootfs_path/usr/sbin/selinuxenabled.lxcorig | |
ln -s /bin/false $rootfs_path/usr/sbin/selinuxenabled | |
fi | |
# This is a known problem and documented in RedHat bugzilla as relating | |
# to a problem with auditing enabled. This prevents an error in | |
# the container "Cannot make/remove an entry for the specified session" | |
sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/login | |
sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/sshd | |
if [ -f ${rootfs_path}/etc/pam.d/crond ] | |
then | |
sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/crond | |
fi | |
# In addition to disabling pam_loginuid in the above config files | |
# we'll also disable it by linking it to pam_permit to catch any | |
# we missed or any that get installed after the container is built. | |
# | |
# Catch either or both 32 and 64 bit archs. | |
if [ -f ${rootfs_path}/lib/security/pam_loginuid.so ] | |
then | |
( cd ${rootfs_path}/lib/security/ | |
mv pam_loginuid.so pam_loginuid.so.disabled | |
ln -s pam_permit.so pam_loginuid.so | |
) | |
fi | |
if [ -f ${rootfs_path}/lib64/security/pam_loginuid.so ] | |
then | |
( cd ${rootfs_path}/lib64/security/ | |
mv pam_loginuid.so pam_loginuid.so.disabled | |
ln -s pam_permit.so pam_loginuid.so | |
) | |
fi | |
# Set default localtime to the host localtime if not set... | |
if [ -e /etc/localtime -a ! -e ${rootfs_path}/etc/localtime ] | |
then | |
# if /etc/localtime is a symlink, this should preserve it. | |
cp -a /etc/localtime ${rootfs_path}/etc/localtime | |
fi | |
# Deal with some dain bramage in the /etc/init.d/halt script. | |
# Trim it and make it our own and link it in before the default | |
# halt script so we can intercept it. This also preventions package | |
# updates from interferring with our interferring with it. | |
# | |
# There's generally not much in the halt script that useful but what's | |
# in there from resetting the hardware clock down is generally very bad. | |
# So we just eliminate the whole bottom half of that script in making | |
# ourselves a copy. That way a major update to the init scripts won't | |
# trash what we've set up. | |
if [ -f ${rootfs_path}/etc/init.d/halt ] | |
then | |
sed -e '/hwclock/,$d' \ | |
< ${rootfs_path}/etc/init.d/halt \ | |
> ${rootfs_path}/etc/init.d/lxc-halt | |
echo '$command -f' >> ${rootfs_path}/etc/init.d/lxc-halt | |
chmod 755 ${rootfs_path}/etc/init.d/lxc-halt | |
# Link them into the rc directories... | |
( | |
cd ${rootfs_path}/etc/rc.d/rc0.d | |
ln -s ../init.d/lxc-halt S00lxc-halt | |
cd ${rootfs_path}/etc/rc.d/rc6.d | |
ln -s ../init.d/lxc-halt S00lxc-reboot | |
) | |
fi | |
# configure the network using the dhcp | |
cat <<EOF > ${rootfs_path}/etc/sysconfig/network-scripts/ifcfg-eth0 | |
DEVICE=eth0 | |
BOOTPROTO=dhcp | |
ONBOOT=yes | |
HOSTNAME=${UTSNAME} | |
NM_CONTROLLED=no | |
TYPE=Ethernet | |
MTU=${MTU} | |
DHCP_HOSTNAME=$name | |
EOF | |
# set the hostname | |
cat <<EOF > ${rootfs_path}/etc/sysconfig/network | |
NETWORKING=yes | |
HOSTNAME=${UTSNAME} | |
EOF | |
# set minimal hosts | |
cat <<EOF > $rootfs_path/etc/hosts | |
127.0.0.1 localhost $name | |
EOF | |
# set minimal fstab | |
cat <<EOF > $rootfs_path/etc/fstab | |
/dev/root / rootfs defaults 0 0 | |
EOF | |
# create lxc compatibility init script | |
if [ "$release" = "6" ]; then | |
cat <<EOF > $rootfs_path/etc/init/lxc-sysinit.conf | |
start on startup | |
env container | |
pre-start script | |
if [ "x\$container" != "xlxc" -a "x\$container" != "xlibvirt" ]; then | |
stop; | |
fi | |
rm -f /var/lock/subsys/* | |
rm -f /var/run/*.pid | |
[ -e /etc/mtab ] || ln -s /proc/mounts /etc/mtab | |
mkdir -p /dev/shm | |
mount -t tmpfs -o nosuid,nodev tmpfs /dev/shm | |
initctl start tty TTY=console | |
telinit 3 | |
exit 0 | |
end script | |
EOF | |
elif [ "$release" = "5" ]; then | |
cat <<EOF > $rootfs_path/etc/rc.d/lxc.sysinit | |
#! /bin/bash | |
rm -f /etc/mtab /var/run/*.{pid,lock} /var/lock/subsys/* | |
rm -rf {/,/var}/tmp/* | |
echo "/dev/root / rootfs defaults 0 0" > /etc/mtab | |
exit 0 | |
EOF | |
chmod 755 $rootfs_path/etc/rc.d/lxc.sysinit | |
sed -i 's|si::sysinit:/etc/rc.d/rc.sysinit|si::bootwait:/etc/rc.d/lxc.sysinit|' $rootfs_path/etc/inittab | |
# prevent mingetty from calling vhangup(2) since it fails with userns. | |
# Same issue as oracle template: prevent mingetty from calling vhangup(2) | |
# commit 2e83f7201c5d402478b9849f0a85c62d5b9f1589. | |
sed -i 's|^1:|co:2345:respawn:/sbin/mingetty --nohangup console\n1:|' $rootfs_path/etc/inittab | |
sed -i 's|^\([56]:\)|#\1|' $rootfs_path/etc/inittab | |
fi | |
dev_path="${rootfs_path}/dev" | |
rm -rf $dev_path | |
mkdir -p $dev_path | |
mknod -m 666 ${dev_path}/null c 1 3 | |
mknod -m 666 ${dev_path}/zero c 1 5 | |
mknod -m 666 ${dev_path}/random c 1 8 | |
mknod -m 666 ${dev_path}/urandom c 1 9 | |
mkdir -m 755 ${dev_path}/pts | |
mkdir -m 1777 ${dev_path}/shm | |
mknod -m 666 ${dev_path}/tty c 5 0 | |
mknod -m 666 ${dev_path}/tty0 c 4 0 | |
mknod -m 666 ${dev_path}/tty1 c 4 1 | |
mknod -m 666 ${dev_path}/tty2 c 4 2 | |
mknod -m 666 ${dev_path}/tty3 c 4 3 | |
mknod -m 666 ${dev_path}/tty4 c 4 4 | |
mknod -m 600 ${dev_path}/console c 5 1 | |
mknod -m 666 ${dev_path}/full c 1 7 | |
mknod -m 600 ${dev_path}/initctl p | |
mknod -m 666 ${dev_path}/ptmx c 5 2 | |
# setup console and tty[1-4] for login. note that /dev/console and | |
# /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and | |
# /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks. | |
# lxc will maintain these links and bind mount ptys over /dev/lxc/* | |
# since lxc.devttydir is specified in the config. | |
# allow root login on console, tty[1-4], and pts/0 for libvirt | |
echo "# LXC (Linux Containers)" >>${rootfs_path}/etc/securetty | |
echo "lxc/console" >>${rootfs_path}/etc/securetty | |
echo "lxc/tty1" >>${rootfs_path}/etc/securetty | |
echo "lxc/tty2" >>${rootfs_path}/etc/securetty | |
echo "lxc/tty3" >>${rootfs_path}/etc/securetty | |
echo "lxc/tty4" >>${rootfs_path}/etc/securetty | |
echo "# For libvirt/Virtual Machine Monitor" >>${rootfs_path}/etc/securetty | |
echo "pts/0" >>${rootfs_path}/etc/securetty | |
# prevent mingetty from calling vhangup(2) since it fails with userns. | |
# Same issue as oracle template: prevent mingetty from calling vhangup(2) | |
# commit 2e83f7201c5d402478b9849f0a85c62d5b9f1589. | |
if [ -e $container_rootfs/etc/init/tty.conf ] | |
then | |
sed -i 's|mingetty|mingetty --nohangup|' $container_rootfs/etc/init/tty.conf | |
fi | |
if [ ${root_display_password} = "yes" ] | |
then | |
echo "Setting root password to '$root_password'" | |
fi | |
if [ ${root_store_password} = "yes" ] | |
then | |
touch ${config_path}/tmp_root_pass | |
chmod 600 ${config_path}/tmp_root_pass | |
echo ${root_password} > ${config_path}/tmp_root_pass | |
echo "Storing root password in '${config_path}/tmp_root_pass'" | |
fi | |
echo "root:$root_password" | chroot $rootfs_path chpasswd | |
if [ ${root_expire_password} = "yes" ] | |
then | |
# Also set this password as expired to force the user to change it! | |
chroot $rootfs_path passwd -e root | |
fi | |
# This will need to be enhanced for CentOS 7 when systemd | |
# comes into play... /\/\|=mhw=|\/\/ | |
return 0 | |
} | |
configure_centos_init() | |
{ | |
sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.sysinit | |
sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.d/rc.sysinit | |
if [ "$release" = "6" ]; then | |
chroot ${rootfs_path} chkconfig udev-post off | |
fi | |
chroot ${rootfs_path} chkconfig network on | |
if [ -d ${rootfs_path}/etc/init ] | |
then | |
# This is to make upstart honor SIGPWR | |
cat <<EOF >${rootfs_path}/etc/init/power-status-changed.conf | |
# power-status-changed - shutdown on SIGPWR | |
# | |
start on power-status-changed | |
exec /sbin/shutdown -h now "SIGPWR received" | |
EOF | |
fi | |
} | |
configure_centos_systemd() | |
{ | |
rm -f ${rootfs_path}/etc/systemd/system/default.target | |
touch ${rootfs_path}/etc/fstab | |
chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/systemd-udevd.service | |
chroot ${rootfs_path} ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target | |
# Make systemd honor SIGPWR | |
chroot ${rootfs_path} ln -s /usr/lib/systemd/system/halt.target /etc/systemd/system/sigpwr.target | |
#dependency on a device unit fails it specially that we disabled udev | |
# sed -i 's/After=dev-%i.device/After=/' ${rootfs_path}/lib/systemd/system/getty\@.service | |
# | |
# Actually, the After=dev-%i.device line does not appear in the | |
# Fedora 17 or Fedora 18 systemd getty\@.service file. It may be left | |
# over from an earlier version and it's not doing any harm. We do need | |
# to disable the "ConditionalPathExists=/dev/tty0" line or no gettys are | |
# started on the ttys in the container. Lets do it in an override copy of | |
# the service so it can still pass rpm verifies and not be automatically | |
# updated by a new systemd version. -- mhw /\/\|=mhw=|\/\/ | |
sed -e 's/^ConditionPathExists=/# ConditionPathExists=/' \ | |
-e 's/After=dev-%i.device/After=/' \ | |
< ${rootfs_path}/lib/systemd/system/getty\@.service \ | |
> ${rootfs_path}/etc/systemd/system/getty\@.service | |
# Setup getty service on the 4 ttys we are going to allow in the | |
# default config. Number should match lxc.tty | |
( cd ${rootfs_path}/etc/systemd/system/getty.target.wants | |
for i in 1 2 3 4 ; do ln -sf ../getty\@.service getty@tty${i}.service; done ) | |
} | |
download_centos() | |
{ | |
# check the mini centos was not already downloaded | |
INSTALL_ROOT=$cache/partial | |
mkdir -p $INSTALL_ROOT | |
if [ $? -ne 0 ]; then | |
echo "Failed to create '$INSTALL_ROOT' directory" | |
return 1 | |
fi | |
# download a mini centos into a cache | |
echo "Downloading centos minimal ..." | |
YUM="yum --installroot $INSTALL_ROOT -y --nogpgcheck --releasever=$release" | |
PKG_LIST="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils" | |
# use temporary repository definition | |
REPO_FILE=$INSTALL_ROOT/etc/yum.repos.d/lxc-centos-temp.repo | |
mkdir -p $(dirname $REPO_FILE) | |
if [ -n "$repo" ]; then | |
cat <<EOF > $REPO_FILE | |
[base] | |
name=local repository | |
baseurl="$repo" | |
EOF | |
else | |
cat <<EOF > $REPO_FILE | |
[base] | |
name=CentOS-$release - Base | |
mirrorlist=http://mirrorlist.centos.org/?release=$release&arch=$basearch&repo=os | |
[updates] | |
name=CentOS-$release - Updates | |
mirrorlist=http://mirrorlist.centos.org/?release=$release&arch=$basearch&repo=updates | |
EOF | |
fi | |
# create minimal device nodes, needed for "yum install" and "yum update" process | |
mkdir -p $INSTALL_ROOT/dev | |
force_mknod 666 $INSTALL_ROOT/dev/null c 1 3 | |
force_mknod 666 $INSTALL_ROOT/dev/urandom c 1 9 | |
$YUM install $PKG_LIST | |
if [ $? -ne 0 ]; then | |
echo "Failed to download the rootfs, aborting." | |
return 1 | |
fi | |
# use same nameservers as hosts, needed for "yum update later" | |
cp /etc/resolv.conf $INSTALL_ROOT/etc/ | |
# check whether rpmdb is under $HOME | |
if [ ! -e $INSTALL_ROOT/var/lib/rpm/Packages -a -e $INSTALL_ROOT/$HOME/.rpmdb/Packages ]; then | |
echo "Fixing rpmdb location ..." | |
mv $INSTALL_ROOT/$HOME/.rpmdb/[A-Z]* $INSTALL_ROOT/var/lib/rpm/ | |
rm -rf $INSTALL_ROOT/$HOME/.rpmdb | |
chroot $INSTALL_ROOT rpm --rebuilddb 2>/dev/null | |
fi | |
# check whether rpmdb version is correct | |
chroot $INSTALL_ROOT rpm --quiet -q yum 2>/dev/null | |
ret=$? | |
# if "rpm -q" doesn't work due to rpmdb version difference, | |
# then we need to redo the process using the newly-installed yum | |
if [ $ret -gt 0 ]; then | |
echo "Reinstalling packages ..." | |
mv $REPO_FILE $REPO_FILE.tmp | |
mkdir $INSTALL_ROOT/etc/yum.repos.disabled | |
mv $INSTALL_ROOT/etc/yum.repos.d/*.repo $INSTALL_ROOT/etc/yum.repos.disabled/ | |
mv $REPO_FILE.tmp $REPO_FILE | |
mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/etc | |
cp /etc/resolv.conf $INSTALL_ROOT/$INSTALL_ROOT/etc/ | |
mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/dev | |
mknod -m 666 $INSTALL_ROOT/$INSTALL_ROOT/dev/null c 1 3 | |
mknod -m 666 $INSTALL_ROOT/$INSTALL_ROOT/dev/urandom c 1 9 | |
mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/var/cache/yum | |
cp -al $INSTALL_ROOT/var/cache/yum/* $INSTALL_ROOT/$INSTALL_ROOT/var/cache/yum/ | |
chroot $INSTALL_ROOT $YUM install $PKG_LIST | |
if [ $? -ne 0 ]; then | |
echo "Failed to download the rootfs, aborting." | |
return 1 | |
fi | |
mv $INSTALL_ROOT/$INSTALL_ROOT $INSTALL_ROOT.tmp | |
rm -rf $INSTALL_ROOT | |
mv $INSTALL_ROOT.tmp $INSTALL_ROOT | |
fi | |
rm -f $REPO_FILE | |
rm -rf $INSTALL_ROOT/var/cache/yum/* | |
mv "$INSTALL_ROOT" "$cache/rootfs" | |
echo "Download complete." | |
return 0 | |
} | |
copy_centos() | |
{ | |
# make a local copy of the mini centos | |
echo -n "Copying rootfs to $rootfs_path ..." | |
#cp -a $cache/rootfs-$arch $rootfs_path || return 1 | |
# i prefer rsync (no reason really) | |
mkdir -p $rootfs_path | |
rsync -a $cache/rootfs/ $rootfs_path/ | |
echo | |
return 0 | |
} | |
update_centos() | |
{ | |
mount -o bind /dev ${cache}/rootfs/dev | |
mount -t proc proc ${cache}/rootfs/proc | |
# Always make sure /etc/resolv.conf is up to date in the target! | |
cp /etc/resolv.conf ${cache}/rootfs/etc/ | |
chroot ${cache}/rootfs yum -y update | |
umount ${cache}/rootfs/proc | |
umount ${cache}/rootfs/dev | |
} | |
install_centos() | |
{ | |
mkdir -p /var/lock/subsys/ | |
( | |
flock -x 9 | |
if [ $? -ne 0 ]; then | |
echo "Cache repository is busy." | |
return 1 | |
fi | |
echo "Checking cache download in $cache/rootfs ... " | |
if [ ! -e "$cache/rootfs" ]; then | |
download_centos | |
if [ $? -ne 0 ]; then | |
echo "Failed to download 'centos base'" | |
return 1 | |
fi | |
else | |
echo "Cache found. Updating..." | |
update_centos | |
if [ $? -ne 0 ]; then | |
echo "Failed to update 'centos base', continuing with last known good cache" | |
else | |
echo "Update finished" | |
fi | |
fi | |
echo "Copy $cache/rootfs to $rootfs_path ... " | |
copy_centos | |
if [ $? -ne 0 ]; then | |
echo "Failed to copy rootfs" | |
return 1 | |
fi | |
return 0 | |
) 9>/var/lock/subsys/lxc-centos | |
return $? | |
} | |
create_hwaddr() | |
{ | |
openssl rand -hex 5 | sed -e 's/\(..\)/:\1/g; s/^/fe/' | |
} | |
copy_configuration() | |
{ | |
mkdir -p $config_path | |
grep -q "^lxc.rootfs" $config_path/config 2>/dev/null || echo " | |
lxc.rootfs = $rootfs_path | |
" >> $config_path/config | |
# The following code is to create static MAC addresses for each | |
# interface in the container. This code will work for multiple | |
# interfaces in the default config. | |
mv $config_path/config $config_path/config.def | |
while read LINE | |
do | |
# This should catch variable expansions from the default config... | |
if expr "${LINE}" : '.*\$' > /dev/null 2>&1 | |
then | |
LINE=$(eval "echo \"${LINE}\"") | |
fi | |
# There is a tab and a space in the regex bracket below! | |
# Seems that \s doesn't work in brackets. | |
KEY=$(expr "${LINE}" : '\s*\([^ ]*\)\s*=') | |
if [[ "${KEY}" != "lxc.network.hwaddr" ]] | |
then | |
echo ${LINE} >> $config_path/config | |
if [[ "${KEY}" == "lxc.network.link" ]] | |
then | |
echo "lxc.network.hwaddr = $(create_hwaddr)" >> $config_path/config | |
fi | |
fi | |
done < $config_path/config.def | |
rm -f $config_path/config.def | |
if [ -e "/usr/share/lxc/config/centos.common.conf" ]; then | |
echo " | |
# Include common configuration | |
lxc.include = /usr/share/lxc/config/centos.common.conf | |
" >> $config_path/config | |
fi | |
# Append things which require expansion here... | |
cat <<EOF >> $config_path/config | |
lxc.arch = $arch | |
lxc.utsname = $utsname | |
# When using LXC with apparmor, uncomment the next line to run unconfined: | |
#lxc.aa_profile = unconfined | |
# example simple networking setup, uncomment to enable | |
#lxc.network.type = $lxc_network_type | |
#lxc.network.flags = up | |
#lxc.network.link = $lxc_network_link | |
#lxc.network.name = eth0 | |
# Additional example for veth network type | |
# static MAC address, | |
#lxc.network.hwaddr = 00:16:3e:77:52:20 | |
# persistent veth device name on host side | |
# Note: This may potentially collide with other containers of same name! | |
#lxc.network.veth.pair = v-$name-e0 | |
EOF | |
if [ $? -ne 0 ]; then | |
echo "Failed to add configuration" | |
return 1 | |
fi | |
return 0 | |
} | |
clean() | |
{ | |
if [ ! -e $cache ]; then | |
exit 0 | |
fi | |
# lock, so we won't purge while someone is creating a repository | |
( | |
flock -x 9 | |
if [ $? != 0 ]; then | |
echo "Cache repository is busy." | |
exit 1 | |
fi | |
echo -n "Purging the download cache for centos-$release..." | |
rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 | |
exit 0 | |
) 9>/var/lock/subsys/lxc-centos | |
} | |
usage() | |
{ | |
cat <<EOF | |
usage: | |
$1 -n|--name=<container_name> | |
[-p|--path=<path>] [-c|--clean] [-R|--release=<CentOS_release>] [-a|--arch=<arch of the container>] | |
[-h|--help] | |
Mandatory args: | |
-n,--name container name, used to as an identifier for that container from now on | |
Optional args: | |
-p,--path path to where the container rootfs will be created, defaults to /var/lib/lxc/name. | |
-c,--clean clean the cache | |
-R,--release Centos release for the new container. if the host is Centos, then it will defaultto the host's release. | |
--fqdn fully qualified domain name (FQDN) for DNS and system naming | |
--repo repository to use (url) | |
-a,--arch Define what arch the container will be [i686,x86_64] | |
-h,--help print this help | |
EOF | |
return 0 | |
} | |
options=$(getopt -o a:hp:n:cR: -l help,path:,rootfs:,name:,clean,release:,repo:,arch:,fqdn: -- "$@") | |
if [ $? -ne 0 ]; then | |
usage $(basename $0) | |
exit 1 | |
fi | |
arch=$(uname -m) | |
eval set -- "$options" | |
while true | |
do | |
case "$1" in | |
-h|--help) usage $0 && exit 0;; | |
-p|--path) path=$2; shift 2;; | |
--rootfs) rootfs_path=$2; shift 2;; | |
-n|--name) name=$2; shift 2;; | |
-c|--clean) clean=1; shift 1;; | |
-R|--release) release=$2; shift 2;; | |
--repo) repo="$2"; shift 2;; | |
-a|--arch) newarch=$2; shift 2;; | |
--fqdn) utsname=$2; shift 2;; | |
--) shift 1; break ;; | |
*) break ;; | |
esac | |
done | |
if [ ! -z "$clean" -a -z "$path" ]; then | |
clean || exit 1 | |
exit 0 | |
fi | |
basearch=${arch} | |
# Map a few architectures to their generic CentOS repository archs. | |
# The two ARM archs are a bit of a guesstimate for the v5 and v6 | |
# archs. V6 should have hardware floating point (Rasberry Pi). | |
# The "arm" arch is safer (no hardware floating point). So | |
# there may be cases where we "get it wrong" for some v6 other | |
# than RPi. | |
case "$arch" in | |
i686) basearch=i386 ;; | |
armv3l|armv4l|armv5l) basearch=arm ;; | |
armv6l|armv7l|armv8l) basearch=armhfp ;; | |
*) ;; | |
esac | |
# Somebody wants to specify an arch. This is very limited case. | |
# i386/i586/i686 on i386/x86_64 | |
# - or - | |
# x86_64 on x86_64 | |
if [ "${newarch}" != "" -a "${newarch}" != "${arch}" ] | |
then | |
case "${newarch}" in | |
i386|i586|i686) | |
if [ "${basearch}" = "i386" -o "${basearch}" = "x86_64" ] | |
then | |
# Make the arch a generic x86 32 bit... | |
arch=${newarch} | |
basearch=i386 | |
else | |
basearch=bad | |
fi | |
;; | |
*) | |
basearch=bad | |
;; | |
esac | |
if [ "${basearch}" = "bad" ] | |
then | |
echo "You cannot build a ${newarch} CentOS container on a ${arch} host. Sorry!" | |
exit 1 | |
fi | |
fi | |
cache_base=/var/cache/lxc/centos/$basearch | |
# Let's do something better for the initial root password. | |
# It's not perfect but it will defeat common scanning brute force | |
# attacks in the case where ssh is exposed. It will also be set to | |
# expired, forcing the user to change it at first login. | |
if [ "${root_password}" = "" ] | |
then | |
root_password=Root-${name}-${RANDOM} | |
else | |
# If it's got a ding in it, try and expand it! | |
if [ $(expr "${root_password}" : '.*$.') != 0 ] | |
then | |
root_password=$(eval echo "${root_password}") | |
fi | |
# If it has more than 3 consecutive X's in it, feed it | |
# through mktemp as a template. | |
if [ $(expr "${root_password}" : '.*XXXX') != 0 ] | |
then | |
root_password=$(mktemp -u ${root_password}) | |
fi | |
fi | |
if [ -z "${utsname}" ]; then | |
utsname=${name} | |
fi | |
# This follows a standard "resolver" convention that an FQDN must have | |
# at least two dots or it is considered a local relative host name. | |
# If it doesn't, append the dns domain name of the host system. | |
# | |
# This changes one significant behavior when running | |
# "lxc_create -n Container_Name" without using the | |
# --fqdn option. | |
# | |
# Old behavior: | |
# utsname and hostname = Container_Name | |
# New behavior: | |
# utsname and hostname = Container_Name.Domain_Name | |
if [ $(expr "$utsname" : '.*\..*\.') = 0 ]; then | |
if [[ "$(dnsdomainname)" != "" && "$(dnsdomainname)" != "localdomain" ]]; then | |
utsname=${utsname}.$(dnsdomainname) | |
fi | |
fi | |
type yum >/dev/null 2>&1 | |
if [ $? -ne 0 ]; then | |
echo "'yum' command is missing" | |
exit 1 | |
fi | |
if [ -z "$path" ]; then | |
path=$default_path/$name | |
fi | |
if [ -z "$release" ]; then | |
if [ "$is_centos" -a "$centos_host_ver" ]; then | |
release=$centos_host_ver | |
elif [ "$is_redhat" -a "$redhat_host_ver" ]; then | |
# This is needed to clean out bullshit like 6workstation and 6server. | |
release=$(expr $redhat_host_ver : '\([0-9.]*\)') | |
else | |
echo "This is not a CentOS or Redhat host and release is missing, defaulting to 6 use -R|--release to specify release" | |
release=6 | |
fi | |
fi | |
if [ "$(id -u)" != "0" ]; then | |
echo "This script should be run as 'root'" | |
exit 1 | |
fi | |
if [ -z "$rootfs_path" ]; then | |
rootfs_path=$path/rootfs | |
# check for 'lxc.rootfs' passed in through default config by lxc-create | |
if grep -q '^lxc.rootfs' $path/config 2>/dev/null ; then | |
rootfs_path=$(sed -e '/^lxc.rootfs\s*=/!d' -e 's/\s*#.*//' \ | |
-e 's/^lxc.rootfs\s*=\s*//' -e q $path/config) | |
fi | |
fi | |
config_path=$path | |
cache=$cache_base/$release | |
revert() | |
{ | |
echo "Interrupted, so cleaning up" | |
lxc-destroy -n $name | |
# maybe was interrupted before copy config | |
rm -rf $path | |
echo "exiting..." | |
exit 1 | |
} | |
trap revert SIGHUP SIGINT SIGTERM | |
copy_configuration | |
if [ $? -ne 0 ]; then | |
echo "failed write configuration file" | |
exit 1 | |
fi | |
install_centos | |
if [ $? -ne 0 ]; then | |
echo "failed to install centos" | |
exit 1 | |
fi | |
configure_centos | |
if [ $? -ne 0 ]; then | |
echo "failed to configure centos for a container" | |
exit 1 | |
fi | |
if [ -d ${rootfs_path}/etc/systemd/system ] | |
then | |
configure_centos_systemd | |
fi | |
if [ -f ${rootfs_path}/etc/rc.sysinit ] | |
then | |
configure_centos_init | |
fi | |
if [ ! -z "$clean" ]; then | |
clean || exit 1 | |
exit 0 | |
fi | |
echo " | |
Container rootfs and config have been created. | |
Edit the config file to check/enable networking setup. | |
" | |
if [ ${root_display_password} = "yes" ] | |
then | |
echo "The temporary password for root is: '$root_password' | |
You may want to note that password down before starting the container. | |
" | |
fi | |
if [ ${root_store_password} = "yes" ] | |
then | |
echo "The temporary root password is stored in: | |
'${config_path}/tmp_root_pass' | |
" | |
fi | |
if [ ${root_prompt_password} = "yes" ] | |
then | |
echo "Invoking the passwd command in the container to set the root password. | |
chroot ${rootfs_path} passwd | |
" | |
chroot ${rootfs_path} passwd | |
else | |
if [ ${root_expire_password} = "yes" ] | |
then | |
echo " | |
The root password is set up as "expired" and will require it to be changed | |
at first login, which you should do as soon as possible. If you lose the | |
root password or wish to change it without starting the container, you | |
can change it from the host by running the following command (which will | |
also reset the expired flag): | |
chroot ${rootfs_path} passwd | |
" | |
fi | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment