From 54080484c00585c0e2844d947613f6f0d751493e Mon Sep 17 00:00:00 2001 From: Nikos Skalkotos Date: Wed, 28 Sep 2011 10:58:42 +0300 Subject: [PATCH] Add initial helper tasks Use SNF_IMAGE_ as prefix for the floppy exporting variables --- helper/common.sh | 122 ++++++++++++++++++++++++++++++++++ helper/rc.local | 70 +++++++++++++++++++ helper/snf-passtohash.py | 68 +++++++++++++++++++ helper/tasks/00InstallUnattend.sh | 24 +++++++ helper/tasks/40AssignHostname.sh | 78 ++++++++++++++++++++++ helper/tasks/40ChangePassword.sh | 82 +++++++++++++++++++++++ helper/tasks/40DeleteSSHKeys.sh | 33 +++++++++ helper/tasks/90SELinuxAutorelabel.sh | 31 +++++++++ host/common.sh.in | 2 +- host/create | 2 +- 10 files changed, 510 insertions(+), 2 deletions(-) create mode 100644 helper/common.sh create mode 100644 helper/rc.local create mode 100755 helper/snf-passtohash.py create mode 100644 helper/tasks/00InstallUnattend.sh create mode 100644 helper/tasks/40AssignHostname.sh create mode 100644 helper/tasks/40ChangePassword.sh create mode 100644 helper/tasks/40DeleteSSHKeys.sh create mode 100644 helper/tasks/90SELinuxAutorelabel.sh diff --git a/helper/common.sh b/helper/common.sh new file mode 100644 index 0000000..cca0630 --- /dev/null +++ b/helper/common.sh @@ -0,0 +1,122 @@ +# Copyright 2011 GRNET S.A. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# The views and conclusions contained in the software and documentation are +# those of the authors and should not be interpreted as representing official +# policies, either expressed or implied, of GRNET S.A. + +OUTPUT=/dev/ttyS0 +RESULT=/dev/ttyS2 + +# Programs +XMLSTARLET=xmlstarlet +INSTALL_MBR=install-mbr + +CLEANUP=( ) + +log_error() { + echo "$@" >&2 + exit 1 +} + +get_base_distro() { + local root_dir=$1 + + if [ -e "$root_dir/etc/debian_version" ]; then + echo "debian" + elif [ -e "$root_dir/etc/redhat-release" ]; then + echo "redhat" + elif [ -e "$root_dir/etc/slackware-version" ]; then + echo "slackware" + elif [ -e "$root_dir/SuSE-release" ]; then + echo "suse" + elif [ -e "$root_dir/gentoo-release" ]; then + echo "gentoo" + fi +} + +get_distro() { + local root_dir=$1 + + if [ -e "$root_dir/etc/debian_version" ]; then + distro="debian" + if [ -e ${root_dir}/etc/lsb-release ]; then + ID=$(grep $DISTRIB_ID=${root_dir}/etc/lsb-release | cut -d= -f2) + if [ "x$ID" = "xUbuntu" ]; then + distro="ubuntu" + fi + fi + echo "$distro" + elif [ -e "$root_dir/etc/fedora-release" ]; then + echo "fedora" + elif [ -e "$root_dir/etc/centos-release" ]; thne + echo "centos" + elif [ -e "$root_dir/etc/redhat-release" ]; then + echo "redhat" + elif [ -e "$root_dir/etc/slackware-version" ]; then + echo "slackware" + elif [ -e "$root_dir/SuSE-release" ]; then + echo "suse" + elif [ -e "$root_dir/gentoo-release" ]; then + echo "gentoo" + fi +} + +cleanup() { +# if something fails here, it souldn't call cleanup again... + trap - EXIT + + if [ ${#CLEANUP[*]} -gt 0 ]; then + LAST_ELEMENT=$((${#CLEANUP[*]}-1)) + REVERSE_INDEXES=$(seq ${LAST_ELEMENT} -1 0) + for i in $REVERSE_INDEXES; do + # If something fails here, it's better to retry it for a few times + # before we give up with an error. This is needed for kpartx when + # dealing with ntfs partitions mounted through fuse. umount is not + # synchronous and may return while the partition is still busy. A + # premature attempt to delete partition mappings through kpartx on a + # device that hosts previously mounted ntfs partition may fail with + # an `device-mapper: remove ioctl failed: Device or resource busy' + # error. A sensible workaround for this is to wait for a while and + # then try again. + local cmd=${CLEANUP[$i]} + $cmd || for interval in 0.25 0.5 1 2 4; do + echo "Command $cmd failed!" + echo "I'll wait for $interval secs and will retry..." + sleep $interval + $cmd && break + done + test $? -eq 1 && { echo "Giving Up..."; exit 1; } + done + fi + echo "Clean UP executed" +} + +trap cleanup EXIT + +# Redirect stdout and stderr +exec &> $OUTPUT + +# vim: set sta sts=4 shiftwidth=4 sw=4 et ai : diff --git a/helper/rc.local b/helper/rc.local new file mode 100644 index 0000000..f0e760d --- /dev/null +++ b/helper/rc.local @@ -0,0 +1,70 @@ +#!/bin/bash + +# Copyright 2011 GRNET S.A. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# The views and conclusions contained in the software and documentation are +# those of the authors and should not be interpreted as representing official +# policies, either expressed or implied, of GRNET S.A. + +. /usr/share/snf-image/common.sh + +# terminate helper vm when the script exits +CLEANUP+=("telinit 0") + +floppy=$(mktemp -d floppy.XXXXXXXX) || exit 1 +CLEANUP+=("rm -f $floppy") + +if [ -e /dev/fd0 ]; then + mount /dev/fd0 $floppy + source $floppy + umount $floppy +else + log_error "Floppy Device is not present!" +fi + +# Mount the instance's partition +target=$(mktemp -d target.XXXXXXXX) || exit 1 +CLEANUP+=("rm -f $target") +mount /dev/sdb $target +CLEANUP+=("umount $target") + +SNF_IMAGE_TARGET=$target + +RUN_PARTS=`which run-parts` +if [ -n "$RUN_PARTS" -a -n "/usr/lib/snf-image/tasks" ]; then + set | egrep ^SNF_IMAGE_\\w+= | cut -d= -f1 | while read line; do + export $line + done + $RUN_PARTS "/usr/lib/snf-image/tasks" +fi + +cleanup +trap - EXIT + +# never called... +exit 0 + +# vim: set sta sts=4 shiftwidth=4 sw=4 et ai : diff --git a/helper/snf-passtohash.py b/helper/snf-passtohash.py new file mode 100755 index 0000000..a4c6f0f --- /dev/null +++ b/helper/snf-passtohash.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# +# Copyright (c) 2011 Greek Research and Technology Network +# +"""Generate a Hash from a given Password + +This program takes a Password as an argument and +returns to standard output a hash followed by a newline. +To do this, it generates internally a random salt. + +""" + +import sys +import crypt + +from string import ascii_letters, digits +from random import choice +from os.path import basename +from optparse import OptionParser + + +# This dictionary maps the hashing algorithm method +# with its as documented in: +# http://www.akkadia.org/drepper/SHA-crypt.txt +HASH_ID_FROM_METHOD = { + 'md5': '1', + 'blowfish': '2a', + 'sun-md5': 'md5', + 'sha256': '5', + 'sha512': '6' +} + +def random_salt(length=8): + pool = ascii_letters + digits + "/" + "." + return ''.join(choice(pool) for i in range(length)) + + +def parse_arguments(input_args): + usage = "usage: %prog [-h] [-m encrypt-method] " + parser = OptionParser(usage=usage) + parser.add_option("-m", "--encrypt-method", + dest="encrypt_method", + type='choice', + default="sha512", + choices=HASH_ID_FROM_METHOD.keys(), + help="encrypt password with ENCRYPT_METHOD [%default] \ + (supported: " + ", ".join(HASH_ID_FROM_METHOD.keys()) +")", + ) + + (opts, args) = parser.parse_args(input_args) + + if len(args) != 1: + parser.error('password is missing') + + return (args[0], opts.encrypt_method) + + +def main(): + (password, method) = parse_arguments(sys.argv[1:]) + salt = random_salt() + hash = crypt.crypt(password, "$"+HASH_ID_FROM_METHOD[method]+"$"+salt) + sys.stdout.write("%s\n" % (hash)) + return 0 + +if __name__ == "__main__": + sys.exit(main()) + +# vim: set sta sts=4 shiftwidth=4 sw=4 et ai : diff --git a/helper/tasks/00InstallUnattend.sh b/helper/tasks/00InstallUnattend.sh new file mode 100644 index 0000000..a1d712e --- /dev/null +++ b/helper/tasks/00InstallUnattend.sh @@ -0,0 +1,24 @@ +#! /bin/sh + +### BEGIN TASK INFO +# Provides: InstallUnattend +# Requires: +# Short-Description: Installs Unattend.xml for unattended windows setup +### END TAST INFO + +set -e +. /usr/share/snf-image/common.sh + +if [ -z "$SNF_IMAGE_TARGET" ]; then + log_error "Target dir is missing" +fi + +if [ "$SNF_IMAGE_TYPE" != "ntfsdump" ]; then + exit 0 +fi + +if [ -e /usr/share/snf-image/unattend.xml ]; then + cat /usr/share/snf-image/unattend.xml > $SNF_IMAGE_TARGET/Unattend.xml +fi + +exit 0 diff --git a/helper/tasks/40AssignHostname.sh b/helper/tasks/40AssignHostname.sh new file mode 100644 index 0000000..a66ed42 --- /dev/null +++ b/helper/tasks/40AssignHostname.sh @@ -0,0 +1,78 @@ +#! /bin/sh + +### BEGIN TASK INFO +# Provides: AssignHostname +# Requires: InstallUnattend +# Short-Description: Assign the Hostname of Computer Name in the instance +### END TAST INFO + +set -e +. /usr/share/snf-image/common.sh + +windows_hostname() { + local target=$1 + local password=$2 + + local tmp_unattend=`mktemp` || exit 1 + CLEANUP+=("rm $tmp_unattend") + + echo -n "Assigning new computer name..." + + local namespace="urn:schemas-microsoft-com:unattend" + + $XMLSTARLET ed -N x=$namespace -u "/x:unattend/x:settings/x:component/x:ComputerName" -v $password "$target/Unattend.xml" > $tmp_unattend + + cat $tmp_unattend > "$target/Unattend.xml" + echo done +} + +linux_hostname() { + local target=$1 + local hostname=$2 + + local distro=$(get_base_distro $target) + + case "$distro" in + debian) + echo "$hostname" > $target/etc/hostname;; + redhat) + sed -ie "s/HOSTNAME=.*$/HOSTNAME=$hostname/g" $target/etc/sysconfig/network;; + slackware|suse) + #local domain=$(sed -e 's/^[^\.]*//g' < /etc/HOSTNAME) + + # In slackware hostname and domain name are joined together. For now I + # will not retain the domain name. + + echo $hostname > ${target}/etc/HOSTNAME;; + gentoo) + sed -ie "s/\(\(HOSTNAME\)\|\(hostname\)\)=.*$/\1=\"$hostname\"/" $target/etc/conf.d/hostname;; + esac + + # Some Linux distributions assign the hostname to 127.0.1.1 in order to be + # resolvable to an IP address. Lets replace this if found in /etc/hosts + sed -ie "s/^[[:blank:]]*127\.0\.1\.1[[:blank:]].\+$/127.0.1.1\t$hostname/" $target/etc/hosts +} + +if [ -z "$SNF_IMAGE_TARGET" -o ! -d "$SNF_IMAGE_TARGET" ]; then + log_error "Missing target directory" +fi + +if [ -z "$SNF_IMAGE_HOSTNAME" ]; then + log_error "Hostname is missing" +fi + +if [ "$SNF_IMAGE_TYPE" = "ntfsdump" ]; then + windows_hostname $SNF_IMAGE_TARGET $SNF_IMAGE_PASSWORD +elif [ "$SNF_IMAGE_TYPE" = "extdump" ]; then + linux_hostname $SNF_IMAGE_TARGET $SNF_IMAGE_PASSWORD +fi + +echo "done" + +cleanup +trap - EXIT + +exit 0 + +# vim: set sta sts=4 shiftwidth=4 sw=4 et ai : + diff --git a/helper/tasks/40ChangePassword.sh b/helper/tasks/40ChangePassword.sh new file mode 100644 index 0000000..f71c35b --- /dev/null +++ b/helper/tasks/40ChangePassword.sh @@ -0,0 +1,82 @@ +#! /bin/sh + +### BEGIN TASK INFO +# Provides: ChangePassword +# Requires: InstallUnattend +# Short-Description: Changes Password for specified users +### END TAST INFO + +set -e +. /usr/share/snf-image/common.sh + +windows_password() { + local target=$1 + local password=$2 + + local tmp_unattend=`mktemp` || exit 1 + CLEANUP+=("rm $tmp_unattend") + + echo -n "Installing new admin password..." + + local namespace="urn:schemas-microsoft-com:unattend" + + $XMLSTARLET ed -N x=$namespace -u "/x:unattend/x:settings/x:component/x:UserAccounts/x:AdministratorPassword/x:Value" -v $password "$target/Unattend.xml" > $tmp_unattend + + cat $tmp_unattend > "$target/Unattend.xml" + echo done +} + +linux_password() { + local target=$1 + local password=$2 + + local hash=$(/usr/share/snf-image/snf-passtohash.py $password) + if [ ! -e ${target}/etc/shadow ]; then + log_error "No /etc/shadow found!" + fi + + declare -a users=("root") + + local distro=$(get_distro $target) + + if [ "x$disto" = "xubuntu" -o \ + "x$disto" + "xfedora" ] ; then + users+=("user") + fi + + for i in $(seq 0 1 $((${#users[@]}-1))); do + local tmp_shadow=$(mktemp) + CLEANUP+=("rm $tmp_shadow") + + echo -n "Setting ${users[$i]} password..." + + echo "${users[$i]}:$hash:15103:0:99999:7:::" > $tmp_shadow + grep -v "${users[$i]}" ${TARGET}/etc/shadow >> $tmp_shadow + cat $tmp_shadow > ${target}/etc/shadow + echo "done" + done +} + +if [ -z "$SNF_IMAGE_TARGET" ]; then + log_error "Target dir is missing" +fi + +if [ -z "$SNF_IMAGE_PASSWORD" ]; then + log_error "Password is missing" +fi + +if [ "$SNF_IMAGE_TYPE" = "ntfsdump" ]; then + windows_password $SNF_IMAGE_TARGET $SNF_IMAGE_PASSWORD +elif [ "$SNF_IMAGE_TYPE" = "extdump" ]; then + linux_password $SNF_IMAGE_TARGET $SNF_IMAGE_PASSWORD +fi + +echo "done" + +cleanup +trap - EXIT + +exit 0 + +# vim: set sta sts=4 shiftwidth=4 sw=4 et ai : + diff --git a/helper/tasks/40DeleteSSHKeys.sh b/helper/tasks/40DeleteSSHKeys.sh new file mode 100644 index 0000000..d8abbbd --- /dev/null +++ b/helper/tasks/40DeleteSSHKeys.sh @@ -0,0 +1,33 @@ +#! /bin/sh + +### BEGIN TASK INFO +# Provides: DeleteSSHKeys +# Requires: +# Short-Description: Remove ssh keys if present. +### END TAST INFO + +set -e +. /usr/share/snf-image/common.sh + + +if [ "$SNF_IMAGE_TYPE" != "extdump" ]; then + exit 0 +fi + +HOST_KEY="/etc/ssh/ssh_host_key" +RSA_KEY="/etc/ssh/ssh_host_rsa_key" +DSA_KEY="/etc/ssh/ssh_host_dsa_key" + +for key in $HOST_KEY $RSA_KEY $DSA_KEY ; do + if [ -f "${SNF_IMAGE_TARGET}/${key}" ] ; then + rm -f ${SNF_IMAGE_TARGET}/${key}* + fi +done + +cleanup +trap - EXIT + +exit 0 + +# vim: set sta sts=4 shiftwidth=4 sw=4 et ai : + diff --git a/helper/tasks/90SELinuxAutorelabel.sh b/helper/tasks/90SELinuxAutorelabel.sh new file mode 100644 index 0000000..c0d7bd6 --- /dev/null +++ b/helper/tasks/90SELinuxAutorelabel.sh @@ -0,0 +1,31 @@ +#! /bin/sh + +### BEGIN TASK INFO +# Provides: SELinuxAutorelabel +# Requires: +# Short-Description: Force the system to relabel at next boot +### END TAST INFO + +set -e +. /usr/share/snf-image/common.sh + +if [ -z "$SNF_IMAGE_TARGET" ]; then + log_error "Target dir is missing" +fi + +if [ "$SNF_IMAGE_TYPE" != "extdump" ]; then + exit 0 +fi + +distro=$(get_base_distro $SNF_IMAGE_TARGET) + +if [ "$distro" = "redhat" ]; then + # we have to force a filesystem relabeling for SELinux after messing + # around with the filesystem in redhat derived OSs + echo "Enforce an automatic relabeling in the initial boot process..." + touch $SNF_IMAGE_TARGET/.autorelabel +fi + +exit 0 + +# vim: set sta sts=4 shiftwidth=4 sw=4 et ai : diff --git a/host/common.sh.in b/host/common.sh.in index 06a56df..0542f7e 100644 --- a/host/common.sh.in +++ b/host/common.sh.in @@ -171,7 +171,7 @@ create_floppy() { dd bs=512 count=2880 if=/dev/zero of=$img mkfs.ext2 -F $img > /dev/null mount $img $target -o loop - set|egrep ^snf_export_\\w+|sed -e 's/^snf_export_/GANETI_/' > $target/rules + set|egrep ^snf_export_\\w+=|sed -e 's/^snf_export_/SNF_IMAGE_/' > $target/rules umount $target } diff --git a/host/create b/host/create index 5bcbeaf..ec6a114 100755 --- a/host/create +++ b/host/create @@ -79,7 +79,7 @@ $MONITOR dd bs=4M if=$IMAGE_FILE of=$root_dev oflag=direct floppy=$(mktemp --tmpdir floppy.XXXXXXXX) || exit 1 CLEANUP+=("rm -f $floppy") -snf_export_FORMAT=${IMG_FORMAT} +snf_export_TYPE=${IMG_FORMAT} snf_export_PASSWORD=${IMG_PASSWD} snf_export_HOSTNAME=${instance} -- 1.7.10.4