lxc-centos.in revision 91c5c53f59262704538a7b8f9571d626e4e08011
#
# template script for generating centos container for LXC
#
# lxc: linux Container library
# Authors:
# Daniel Lezcano <daniel.lezcano@free.fr>
# Ramez Hanna <rhanna@informatiq.org>
# Fajar A. Nugraha <github@fajar.net>
# Michael H. Warfield <mhw@WittsEnd.com>
# 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
# 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 contians 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='Root-${name}-XXXXXX'}
# 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='yes'}
# These are only going into comments in the resulting config...
# 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)
[ "$arg" = "--" ] && break
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
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
then
CPE_NAME=$(head -n1 /etc/system-release-cpe)
if [ "${CPE_URI}" != "cpe:/o" ]
then
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
#
# 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.
then
# Instead we got this: cpe:/o:centos:linux:6
fi
echo "Host CPE ID from /etc/system-release-cpe: ${CPE_NAME}"
fi
fi
if [ "${CPE_NAME}" != "" -a "${ID}" = "centos" -a "${VERSION_ID}" != "" ]
then
is_centos=true
elif [ "${CPE_NAME}" != "" -a "${ID}" = "redhat" -a "${VERSION_ID}" != "" ]
then
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
{
# delete a device node if exists, and create a new one
}
{
# disable selinux in centos
echo 0 > $rootfs_path/selinux/enforce
if [ -f $rootfs_path/etc/selinux/config ]
then
fi
# Nice catch from Dwight Engen in the Oracle template.
# Wantonly plagerized here with much appreciation.
if [ -f $rootfs_path/usr/sbin/selinuxenabled ]; then
fi
# This is a known problem and documented in RedHat bugzilla as relating
# to a problem with auditing enabled. This prevents an error in
if [ -f ${rootfs_path}/etc/pam.d/crond ]
then
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/
)
fi
if [ -f ${rootfs_path}/lib64/security/pam_loginuid.so ]
then
( cd ${rootfs_path}/lib64/security/
)
fi
# Set default localtime to the host localtime if not set...
if [ -e /etc/localtime -a ! -e ${rootfs_path}/etc/localtime ]
then
fi
# 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
< ${rootfs_path}/etc/init.d/halt \
> ${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
cd ${rootfs_path}/etc/rc.d/rc6.d
)
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
none /dev/shm tmpfs nosuid,nodev 0 0
EOF
# create lxc compatibility init script
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
initctl start tty TTY=console
rm -f /var/lock/subsys/*
rm -f /var/run/*.pid
telinit 3
exit 0;
end script
EOF
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
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.
fi
dev_path="${rootfs_path}/dev"
# since lxc.devttydir is specified in the config.
# allow root login on console, tty[1-4], and pts/0 for libvirt
# prevent mingetty from calling vhangup(2) since it fails with userns.
# Same issue as oracle template: prevent mingetty from calling vhangup(2)
# commit 2e83f7201c5d402478b9849f0a85c62d5b9f1589.
then
echo "Setting root password to '$root_password'"
fi
then
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
then
# Also set this password as expired to force the user to change it!
fi
# This will need to be enhanced for CentOS 7 when systemd
# comes into play... /\/\|=mhw=|\/\/
return 0
}
{
fi
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
}
{
# check the mini centos was not already downloaded
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"
PKG_LIST="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils"
# use temporary repository definition
mkdir -p $(dirname $REPO_FILE)
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
force_mknod 666 $INSTALL_ROOT/dev/null c 1 3
force_mknod 666 $INSTALL_ROOT/dev/urandom c 1 9
if [ $? -ne 0 ]; then
echo "Failed to download the rootfs, aborting."
return 1
fi
# use same nameservers as hosts, needed for "yum update later"
# 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/
fi
# check whether rpmdb version is correct
ret=$?
# if "rpm -q" doesn't work due to rpmdb version difference,
# then we need to redo the process using the newly-installed yum
echo "Reinstalling packages ..."
mknod -m 666 $INSTALL_ROOT/$INSTALL_ROOT/dev/null c 1 3
mknod -m 666 $INSTALL_ROOT/$INSTALL_ROOT/dev/urandom c 1 9
if [ $? -ne 0 ]; then
echo "Failed to download the rootfs, aborting."
return 1
fi
fi
rm -f $REPO_FILE
mv "$INSTALL_ROOT" "$cache/rootfs"
echo "Download complete."
return 0
}
{
# make a local copy of the mini centos
echo -n "Copying rootfs to $rootfs_path ..."
# i prefer rsync (no reason really)
rsync -a $cache/rootfs/ $rootfs_path/
echo
return 0
}
{
if [ $? -ne 0 ]; then
return 1
fi
}
{
(
flock -x 9
if [ $? -ne 0 ]; then
echo "Cache repository is busy."
return 1
fi
echo "Checking cache download in $cache/rootfs ... "
if [ $? -ne 0 ]; then
echo "Failed to download 'centos base'"
return 1
fi
else
echo "Cache found. Updating..."
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 ... "
if [ $? -ne 0 ]; then
echo "Failed to copy rootfs"
return 1
fi
return 0
) 9>/var/lock/subsys/lxc-centos
return $?
}
{
}
{
" >> $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.
while read LINE
do
# This should catch variable expansions from the default config...
then
fi
# There is a tab and a space in the regex bracket below!
# Seems that \s doesn't work in brackets.
then
echo ${LINE} >> $config_path/config
then
fi
fi
done < $config_path/config.def
echo "
# Include common configuration
lxc.include = @LXCTEMPLATECONFIG@/centos.common.conf
" >> $config_path/config
fi
# Append things which require expansion here...
cat <<EOF >> $config_path/config
lxc.arch = $arch
lxc.utsname = $utsname
lxc.autodev = $auto_dev
# 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..."
exit 0
) 9>@LOCALSTATEDIR@/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
exit 1
fi
eval set -- "$options"
while true
do
case "$1" in
--) shift 1; break ;;
*) break ;;
esac
done
clean || exit 1
exit 0
fi
# 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.
*) ;;
esac
# Somebody wants to specify an arch. This is very limited case.
# - or -
# x86_64 on x86_64
if [ "${newarch}" != "" -a "${newarch}" != "${arch}" ]
then
case "${newarch}" in
if [ "${basearch}" = "i386" -o "${basearch}" = "x86_64" ]
then
# Make the arch a generic x86 32 bit...
else
fi
;;
*)
;;
esac
if [ "${basearch}" = "bad" ]
then
echo "You cannot build a ${newarch} CentOS container on a ${arch} host. Sorry!"
exit 1
fi
fi
# 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
else
# If it's got a ding in it, try and expand it!
then
fi
# If it has more than 3 consequtive X's in it, feed it
# through mktemp as a template.
then
root_password=$(mktemp -u ${root_password})
fi
fi
if [ -z "${utsname}" ]; then
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
fi
fi
if [ $? -ne 0 ]; then
echo "'yum' command is missing"
exit 1
fi
if [ -z "$path" ]; then
fi
if [ -z "$release" ]; then
if [ "$is_centos" -a "$centos_host_ver" ]; then
elif [ "$is_redhat" -a "$redhat_host_ver" ]; then
# This is needed to clean out bullshit like 6workstation and 6server.
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
# CentOS 7 and above should run systemd. We need autodev enabled to keep
# systemd from causing problems.
#
# There is some ambiguity here due to the differnce between versioning
# of point specific releases such as 6.5 and the rolling release 6. We
# only want the major number here if it's a point release...
auto_dev="1"
else
auto_dev="0"
fi
echo "This script should be run as 'root'"
exit 1
fi
if [ -z "$rootfs_path" ]; then
# check for 'lxc.rootfs' passed in through default config by lxc-create
fi
fi
revert()
{
echo "Interrupted, so cleaning up"
# maybe was interrupted before copy config
echo "exiting..."
exit 1
}
if [ $? -ne 0 ]; then
echo "failed write configuration file"
exit 1
fi
if [ $? -ne 0 ]; then
echo "failed to install centos"
exit 1
fi
if [ $? -ne 0 ]; then
echo "failed to configure centos for a container"
exit 1
fi
if [ ! -z $clean ]; then
clean || exit 1
exit 0
fi
echo "
Container rootfs and config have been created.
"
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
"
else
if [ ${root_expire_password} = "yes" ]
then
echo "
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