1
0
mirror of https://github.com/lxc/lxc-templates.git synced 2024-12-22 06:20:13 +00:00
lxc-templates/templates/lxc-openeuler.in

763 lines
22 KiB
Plaintext
Raw Permalink Normal View History

#!/bin/bash
#
# template script for generating OpenEuler 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>
# 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=@LXCPATH@
# 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='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...
lxc_network_type=veth
lxc_network_link=lxcbr0
# is this OpenEuler?
# 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
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.
ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:\([^:]*\)')
VERSION=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\)')
VERSION_ID=$(echo $VERSION | sed 's/[a-zA-Z]//g')
echo "Host CPE ID from /etc/system-release-cpe: ${CPE_NAME}"
fi
fi
if [ "${ID}" = "openEuler" ] && [ -n "${CPE_NAME}" ] && [ -n "${VERSION_ID}" ]
then
openEuler_host_ver=${VERSION_ID}
is_openEuler=true
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_openEuler()
{
# disable selinux
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
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
# 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=\`hostname\`
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
#set dev nodes
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
local i; for i in $(seq 0 4); do
mknod -m 666 dev/tty$i c 4 $i
chown 0:5 dev/tty$i
done
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.tty.dir is specified in the config.
# allow root login on console, tty[1-4], and pts/0 for libvirt
cat <<EOF >> "${rootfs_path}/etc/securetty"
# LXC (Linux Containers)"
lxc/console
lxc/tty1
lxc/tty2
lxc/tty3
lxc/tty4
# For libvirt/Virtual Machine Monitor
pts/0
EOF
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
return 0
}
arch=$(uname -m)
download_openEuler()
{
# download a mini openEuler into a cache
INSTALL_ROOT=$cache/partial
mkdir -p $INSTALL_ROOT
echo "Downloading OpenEuler minimal ..."
YUM0="yum --installroot $INSTALL_ROOT -y --nogpgcheck"
if yum -h | grep -q 'releasever=RELEASEVER'; then
YUM="$YUM0 --releasever=$release"
else
YUM="$YUM0"
fi
PKG_LIST="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils cronie network-scripts coreutils net-tools iputils"
# use temporary repository definition
# always prefer the repo given by the user
REPO_FILE=$INSTALL_ROOT/etc/yum.repos.d/lxc-openEuler-temp.repo
mkdir -p $(dirname $REPO_FILE)
repo_name=$(echo $PRETTY_NAME | sed 's/[()]//g')
repo_name=$(echo $repo_name | sed 's/ /-/g')
if [ -n "$repo" ]; then
cat <<EOF > $REPO_FILE
[base]
name=local repository
baseurl="$repo"
EOF
else
cat <<EOF > $REPO_FILE
[OS]
name=OS
baseurl=http://repo.openeuler.org/$repo_name/OS/$arch/
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-ANOLIS
gpgcheck=0
[everything]
name=everything
baseurl=http://repo.openeuler.org/$repo_name/everything/$arch/
enabled=0
[EPOL]
name=EPOL
baseurl=http://repo.openeuler.org/$repo_name/EPOL/main/$arch/
enabled=0
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 $YUM0 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_openEuler()
{
# make a local copy of the mini openEuler
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 -SHaAX $cache/rootfs/ $rootfs_path/
echo
return 0
}
update_openEuler()
{
YUM="chroot $cache/rootfs yum -y --nogpgcheck"
$YUM update
if [ $? -ne 0 ]; then
return 1
fi
$YUM clean packages
}
install_openEuler()
{
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_openEuler
if [ $? -ne 0 ]; then
echo "Failed to download 'OpenEuler base'"
return 1
fi
else
echo "Cache found. Updating..."
update_openEuler
if [ $? -ne 0 ]; then
echo "Failed to update 'openEuler base', continuing with last known good cache"
else
echo "Update finished"
fi
fi
echo "Copy $cache/rootfs to $rootfs_path ... "
copy_openEuler
if [ $? -ne 0 ]; then
echo "Failed to copy rootfs"
return 1
fi
return 0
) 9>/var/lock/subsys/lxc-openEuler
return $?
}
create_hwaddr()
{
openssl rand -hex 5 | sed -e 's/\(..\)/:\1/g; s/^/fe/'
}
copy_configuration()
{
mkdir -p $config_path
grep -q "^lxc.rootfs.path" $config_path/config 2>/dev/null || echo "
lxc.rootfs.path = $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.net.0.hwaddr" ]]
then
echo ${LINE} >> $config_path/config
if [[ "${KEY}" == "lxc.net.0.link" ]]
then
echo "lxc.net.0.hwaddr = $(create_hwaddr)" >> $config_path/config
fi
fi
done < $config_path/config.def
rm -f $config_path/config.def
if [ -e "@LXCTEMPLATECONFIG@/openEuler.common.conf" ]; then
echo "
# Include common configuration
lxc.include = @LXCTEMPLATECONFIG@/openEuler.common.conf
" >> $config_path/config
fi
# Append things which require expansion here...
cat <<EOF >> $config_path/config
lxc.arch = $arch
lxc.uts.name = $utsname
# When using LXC with apparmor, uncomment the next line to run unconfined:
#lxc.apparmor.profile = unconfined
# example simple networking setup, uncomment to enable
#lxc.net.0.type = $lxc_network_type
#lxc.net.0.flags = up
#lxc.net.0.link = $lxc_network_link
#lxc.net.0.name = eth0
# Additional example for veth network type
# static MAC address,
#lxc.net.0.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.net.0.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 OpenEuler-$release..."
rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1
exit 0
) 9>@LOCALSTATEDIR@/lock/subsys/lxc-openEuler
}
usage()
{
cat <<EOF
LXC Container configuration for OpenEuler images.
lxc-create --name=NAME --template=TEMPLATE [OPTION...] [-- template-options]
usage:
$1 -n|--name=<container_name>
[-p|--path=<path>] [-c|--clean] [-R|--release=<OpenEuler_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 OpenEuler release for the new container. If the host is OpenEuler, then it will default to the host's release.
--fqdn fully qualified domain name (FQDN) for DNS and system naming
--repo repository to use (url)
-a,--arch The container architecture. Can be one of: x86_64, aarch64, arm32, loongarch64. Defaults to host arch.
-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
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}
# Allow the cache base to be set by environment variable
cache_base=${LXC_CACHE_PATH:-"@LOCALSTATEDIR@/cache/lxc"}/openEuler/$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
release=$openEuler_host_ver
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.path' passed in through default config by lxc-create
if grep -q '^lxc.rootfs.path' $path/config 2>/dev/null ; then
rootfs_path=$(sed -e '/^lxc.rootfs.path\s*=/!d' -e 's/\s*#.*//' \
-e 's/^lxc.rootfs.path\s*=\s*//' -e q $path/config)
fi
fi
config_path=$path
cache=$cache_base/${VERSION_ID}
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_openEuler
if [ $? -ne 0 ]; then
echo "failed to install openEuler"
exit 1
fi
configure_openEuler
if [ $? -ne 0 ]; then
echo "failed to configure OpenEuler for a container"
exit 1
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
if ( mountpoint -q -- "${rootfs_path}" )
then
echo "To reset the root password, you can do:
lxc-start -n ${name}
lxc-attach -n ${name} -- passwd
lxc-stop -n ${name}
"
else
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
fi