diff --git a/imagebuild/tinyipa/Makefile b/imagebuild/tinyipa/Makefile index 6e5a7ef21..7fa83d725 100644 --- a/imagebuild/tinyipa/Makefile +++ b/imagebuild/tinyipa/Makefile @@ -1,4 +1,4 @@ -.PHONY: default all dependencies build finalise iso clean clean_build clean_iso +.PHONY: default all dependencies build finalise addssh iso clean clean_build clean_iso default: dependencies build finalise instance-images all: dependencies build finalise iso instance-images @@ -12,6 +12,9 @@ build: finalise: ./finalise-tinyipa.sh +addssh: + ./add-ssh-tinyipa.sh + iso: ./build-iso.sh @@ -24,9 +27,9 @@ clean_build: sudo -v sudo rm -rf tinyipabuild sudo rm -rf tinyipafinal - rm -f tinyipa*.vmlinuz - rm -f tinyipa*.gz - rm -f tinyipa*.sha256 + rm -f *tinyipa*.vmlinuz + rm -f *tinyipa*.gz + rm -f *tinyipa*.sha256 rm -f build_files/corepure64.gz rm -f build_files/vmlinuz64 rm -f build_files/*.tcz diff --git a/imagebuild/tinyipa/README.rst b/imagebuild/tinyipa/README.rst index 96ca49b93..f18b1e764 100644 --- a/imagebuild/tinyipa/README.rst +++ b/imagebuild/tinyipa/README.rst @@ -92,13 +92,16 @@ before running make or build-tinyipa.sh run:: export PYOPTIMIZE_TINYIPA=false -Enabling SSH access to the ramdisk -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Enabling/disabling SSH access to the ramdisk +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you want to enable SSH access to the image, set ``ENABLE_SSH`` variable in -your shell before building the tinyipa:: +By default tinyipa will be built with OpenSSH server installed but no +public SSH keys authorized to access it. - export ENABLE_SSH=true +If you want to enable SSH access to the image, set ``AUTHORIZE_SSH`` variable +in your shell before building the tinyipa:: + + export AUTHORIZE_SSH=true By default it will use public RSA or DSA keys of the user running the build. To provide other public SSH key, export path to it in your shell before @@ -106,6 +109,23 @@ building tinyipa as follows:: export SSH_PUBLIC_KEY= +If you want to disable SSH altogether, set ``INSTALL_SSH`` variable in your +shell to ``false`` before building the tinyipa:: + + export INSTALL_SSH=false + +You can also rebuild an already built tinyipa image by using ``addssh`` make +tagret:: + + make addssh + +This will fetch the pre-built tinyipa image from "tarballs.openstack.org" +using the version specified as ``BRANCH_NAME`` shell variable as described +above, or it may use an already downloaded ramdisk image if path to it is set +as ``TINYIPA_RAMDISK_FILE`` shell variable before running this make target. +It will install and configure OpenSSH if needed and add public SSH keys for +``tc`` user using the same ``SSH_PUBLIC_KEY`` shell variable as described +above. Enabling biosdevname in the ramdisk ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/imagebuild/tinyipa/add-ssh-tinyipa.sh b/imagebuild/tinyipa/add-ssh-tinyipa.sh new file mode 100755 index 000000000..a1de35065 --- /dev/null +++ b/imagebuild/tinyipa/add-ssh-tinyipa.sh @@ -0,0 +1,148 @@ +#!/bin/bash + +# Rebuild upstream pre-built tinyipa it to be usable with ansible-deploy. +# +# Downloads the pre-built tinyipa ramdisk from tarballs.openstack.org or +# rebuilds a ramdisk under path set as TINYIPA_RAMDISK_FILE shell var. + +# During rebuild this script installs and configures OpenSSH server if needed +# and makes required changes for Ansible + Python to work in compiled/optimized +# Python environment. +# +# By default, id_rsa or id_dsa keys of the user performing the build +# are baked into the image as authorized_keys for 'tc' user. +# To supply different public ssh key, befor running this script set +# SSH_PUBLIC_KEY environment variable to point to absolute path to the key. +# +# This script produces "ansible-" ramdisk that can serve +# as ramdisk for both ansible-deploy driver and agent-based Ironic drivers, + +set -ex +WORKDIR=$(readlink -f $0 | xargs dirname) +REBUILDDIR="$WORKDIR/rebuild" +DST_DIR=$REBUILDDIR +source ${WORKDIR}/common.sh + +source ${WORKDIR}/build_files/tc-mirror.sh +TINYCORE_MIRROR_URL=${TINYCORE_MIRROR_URL:-} +BRANCH_PATH=${BRANCH_PATH:-master} +TINYIPA_RAMDISK_FILE=${TINYIPA_RAMDISK_FILE:-} + +SSH_PUBLIC_KEY=${SSH_PUBLIC_KEY:-} + +TC=1001 +STAFF=50 + +CHROOT_PATH="/tmp/overides:/usr/local/sbin:/usr/local/bin:/apps/bin:/usr/sbin:/usr/bin:/sbin:/bin" +CHROOT_CMD="sudo chroot $REBUILDDIR /usr/bin/env -i PATH=$CHROOT_PATH http_proxy=$http_proxy https_proxy=$https_proxy no_proxy=$no_proxy" +TC_CHROOT_CMD="sudo chroot --userspec=$TC:$STAFF $REBUILDDIR /usr/bin/env -i PATH=$CHROOT_PATH http_proxy=$http_proxy https_proxy=$https_proxy no_proxy=$no_proxy" + +function validate_params { + echo "Validating location of public SSH key" + if [ -n "$SSH_PUBLIC_KEY" ]; then + if [ -r "$SSH_PUBLIC_KEY" ]; then + _found_ssh_key="$SSH_PUBLIC_KEY" + fi + else + for fmt in rsa dsa; do + if [ -r "$HOME/.ssh/id_$fmt.pub" ]; then + _found_ssh_key="$HOME/.ssh/id_$fmt.pub" + break + fi + done + fi + + if [ -z $_found_ssh_key ]; then + echo "Failed to find neither provided nor default SSH key" + exit 1 + fi + + choose_tc_mirror +} + +function get_tinyipa { + if [ -z $TINYIPA_RAMDISK_FILE ]; then + mkdir -p $WORKDIR/build_files/cache + cd $WORKDIR/build_files/cache + wget -N https://tarballs.openstack.org/ironic-python-agent/tinyipa/files/tinyipa-${BRANCH_PATH}.gz + TINYIPA_RAMDISK_FILE="$WORKDIR/build_files/cache/tinyipa-${BRANCH_PATH}.gz" + fi +} + +function unpack_ramdisk { + + if [ -d "$REBUILDDIR" ]; then + sudo rm -rf "$REBUILDDIR" + fi + + mkdir -p "$REBUILDDIR" + + # Extract rootfs from .gz file + ( cd "$REBUILDDIR" && zcat "$TINYIPA_RAMDISK_FILE" | sudo cpio -i -H newc -d ) + +} + +function install_ssh { + if [ ! -f "$REBUILDDIR/usr/local/etc/ssh/sshd_config" ]; then + # tinyipa was built without SSH server installed + # Install and configure bare minimum for SSH access + $TC_CHROOT_CMD tce-load -wic openssh + # Configure OpenSSH + $CHROOT_CMD cp /usr/local/etc/ssh/sshd_config.orig /usr/local/etc/ssh/sshd_config + echo "PasswordAuthentication no" | $CHROOT_CMD tee -a /usr/local/etc/ssh/sshd_config + # Generate and configure host keys - RSA, DSA, Ed25519 + # NOTE(pas-ha) ECDSA host key will still be re-generated fresh on every image boot + $CHROOT_CMD ssh-keygen -q -t rsa -N "" -f /usr/local/etc/ssh/ssh_host_rsa_key + $CHROOT_CMD ssh-keygen -q -t dsa -N "" -f /usr/local/etc/ssh/ssh_host_dsa_key + $CHROOT_CMD ssh-keygen -q -t ed25519 -N "" -f /usr/local/etc/ssh/ssh_host_ed25519_key + echo "HostKey /usr/local/etc/ssh/ssh_host_rsa_key" | $CHROOT_CMD tee -a /usr/local/etc/ssh/sshd_config + echo "HostKey /usr/local/etc/ssh/ssh_host_dsa_key" | $CHROOT_CMD tee -a /usr/local/etc/ssh/sshd_config + echo "HostKey /usr/local/etc/ssh/ssh_host_ed25519_key" | $CHROOT_CMD tee -a /usr/local/etc/ssh/sshd_config + fi + + # setup new user SSH keys anyway + $CHROOT_CMD mkdir -p /home/tc + $CHROOT_CMD chown -R tc.staff /home/tc + $TC_CHROOT_CMD mkdir -p /home/tc/.ssh + cat $_found_ssh_key | $TC_CHROOT_CMD tee /home/tc/.ssh/authorized_keys + $CHROOT_CMD chown tc.staff /home/tc/.ssh/authorized_keys + $TC_CHROOT_CMD chmod 600 /home/tc/.ssh/authorized_keys +} + +function fix_python_optimize { + if grep -q "PYTHONOPTIMIZE=1" "$REBUILDDIR/opt/bootlocal.sh"; then + # tinyipa was built with optimized Python environment, apply fixes + echo "PYTHONOPTIMIZE=1" | $TC_CHROOT_CMD tee -a /home/tc/.ssh/environment + echo "PermitUserEnvironment yes" | $CHROOT_CMD tee -a /usr/local/etc/ssh/sshd_config + echo 'Defaults env_keep += "PYTHONOPTIMIZE"' | $CHROOT_CMD tee -a /etc/sudoers + fi +} + + +function rebuild_ramdisk { + # Rebuild build directory into gz file + ansible_basename="ansible-$(basename $TINYIPA_RAMDISK_FILE)" + ( cd "$REBUILDDIR" && sudo find | sudo cpio -o -H newc | gzip -9 > "$WORKDIR/${ansible_basename}" ) + # Output file created by this script and its size + cd "$WORKDIR" + echo "Produced files:" + du -h "${ansible_basename}" +} + +sudo -v + + +get_tinyipa +unpack_ramdisk +setup_tce "$DST_DIR" + +# NOTE (pas-ha) default tinyipa is built without SSH access, enable it here +install_ssh +# NOTE(pas-ha) default tinyipa is built with PYOPTIMIZE_TINYIPA=true and +# for Ansible+python to work we need to ensure that PYTHONOPTIMIZE=1 is +# set for all sessions from 'tc' user including those that are escalated +# with 'sudo' afterwards +fix_python_optimize + +cleanup_tce $DST_DIR +rebuild_ramdisk diff --git a/imagebuild/tinyipa/finalise-tinyipa.sh b/imagebuild/tinyipa/finalise-tinyipa.sh index 590837ac3..2ff2d6f57 100755 --- a/imagebuild/tinyipa/finalise-tinyipa.sh +++ b/imagebuild/tinyipa/finalise-tinyipa.sh @@ -10,6 +10,15 @@ BUILDDIR="$WORKDIR/tinyipabuild" BUILD_AND_INSTALL_TINYIPA=${BUILD_AND_INSTALL_TINYIPA:-true} TINYCORE_MIRROR_URL=${TINYCORE_MIRROR_URL:-} ENABLE_SSH=${ENABLE_SSH:-false} +INSTALL_SSH=${INSTALL_SSH:-true} +AUTHORIZE_SSH=${ENABLE_SSH:-false} + +if $ENABLE_SSH; then + echo "WARNING: using ENABLE_SSH is deprecated, use INSTALL_SSH and AUTHORIZE_SSH variables instead" + INSTALL_SSH=true + AUTHORIZE_SSH=true +fi + SSH_PUBLIC_KEY=${SSH_PUBLIC_KEY:-} PYOPTIMIZE_TINYIPA=${PYOPTIMIZE_TINYIPA:-true} TINYIPA_REQUIRE_BIOSDEVNAME=${TINYIPA_REQUIRE_BIOSDEVNAME:-false} @@ -17,7 +26,7 @@ TINYIPA_REQUIRE_IPMITOOL=${TINYIPA_REQUIRE_IPMITOOL:-true} echo "Finalising tinyipa:" -if $ENABLE_SSH ; then +if $AUTHORIZE_SSH ; then echo "Validating location of public SSH key" if [ -n "$SSH_PUBLIC_KEY" ]; then if [ -f "$SSH_PUBLIC_KEY" ]; then @@ -81,7 +90,7 @@ while read line; do $TC_CHROOT_CMD tce-load -wic $line done < $WORKDIR/build_files/finalreqs.lst -if $ENABLE_SSH ; then +if $INSTALL_SSH ; then # Install and configure bare minimum for SSH access $TC_CHROOT_CMD tce-load -wic openssh # Configure OpenSSH @@ -97,12 +106,14 @@ if $ENABLE_SSH ; then echo "HostKey /usr/local/etc/ssh/ssh_host_ed25519_key" | $CHROOT_CMD tee -a /usr/local/etc/ssh/sshd_config # setup user and SSH keys - $CHROOT_CMD mkdir -p /home/tc - $CHROOT_CMD chown -R tc.staff /home/tc - $TC_CHROOT_CMD mkdir -p /home/tc/.ssh - cat $_found_ssh_key | $TC_CHROOT_CMD tee /home/tc/.ssh/authorized_keys - $CHROOT_CMD chown tc.staff /home/tc/.ssh/authorized_keys - $TC_CHROOT_CMD chmod 600 /home/tc/.ssh/authorized_keys + if $AUTHORIZE_SSH; then + $CHROOT_CMD mkdir -p /home/tc + $CHROOT_CMD chown -R tc.staff /home/tc + $TC_CHROOT_CMD mkdir -p /home/tc/.ssh + cat $_found_ssh_key | $TC_CHROOT_CMD tee /home/tc/.ssh/authorized_keys + $CHROOT_CMD chown tc.staff /home/tc/.ssh/authorized_keys + $TC_CHROOT_CMD chmod 600 /home/tc/.ssh/authorized_keys + fi fi $TC_CHROOT_CMD tce-load -ic /tmp/builtin/optional/tgt.tcz @@ -141,6 +152,14 @@ if $PYOPTIMIZE_TINYIPA; then set -e find $FINALDIR/usr/local/lib/python2.7 -name "*.py" -not -path "*ironic_python_agent/api/config.py" | sudo xargs --no-run-if-empty rm find $FINALDIR/usr/local/lib/python2.7 -name "*.pyc" | sudo xargs --no-run-if-empty rm + if $INSTALL_SSH && $AUTHORIZE_SSH ; then + # NOTE(pas-ha) for Ansible+Python to work we need to ensure that + # PYTHONOPTIMIZE=1 is set for all sessions from 'tc' user including + # those that are elevated with 'sudo' afterwards + echo "PYTHONOPTIMIZE=1" | $TC_CHROOT_CMD tee -a /home/tc/.ssh/environment + echo "PermitUserEnvironment yes" | $CHROOT_CMD tee -a /usr/local/etc/ssh/sshd_config + echo 'Defaults env_keep += "PYTHONOPTIMIZE"' | $CHROOT_CMD tee -a /etc/sudoers + fi else sudo sed -i "s/PYTHONOPTIMIZE=1/PYTHONOPTIMIZE=0/" "$FINALDIR/opt/bootlocal.sh" fi @@ -148,6 +167,31 @@ fi # Delete unnecessary Babel .dat files find $FINALDIR -path "*babel/locale-data/*.dat" -not -path "*en_US*" | sudo xargs --no-run-if-empty rm +# NOTE(pas-ha) Apparently on TinyCore Ansible's 'command' module is +# not searching for executables in the '/usr/local/(s)bin' paths. +# Thus we symlink everything from there to '/usr/(s)bin' which is being searched, +# so that 'command' module picks full utilities installed by 'util-linux' +# instead of built-in simplified BusyBox ones. +set +x +echo "Symlink all from /usr/local/sbin to /usr/sbin" +pushd "$FINALDIR/usr/local/sbin" +for target in *; do + if [ ! -f "$FINALDIR/usr/sbin/$target" ]; then + $CHROOT_CMD ln -s "/usr/local/sbin/$target" "/usr/sbin/$target" + fi +done +popd +echo "Symlink all from /usr/local/bin to /usr/bin" +# this also includes symlinking Python to the place expected by Ansible +pushd "$FINALDIR/usr/local/bin" +for target in *; do + if [ ! -f "$FINALDIR/usr/bin/$target" ]; then + $CHROOT_CMD ln -s "/usr/local/bin/$target" "/usr/bin/$target" + fi +done +popd +set -x + # Rebuild build directory into gz file ( cd "$FINALDIR" && sudo find | sudo cpio -o -H newc | gzip -9 > "$WORKDIR/tinyipa${BRANCH_EXT}.gz" )