openSUSE Builder

by archie
31 deployments · 15 still active · last rev. 7 months ago

This script customizes the openSUSE distribution provided by Linode.

It allows configuring additional RPM repositories as well as some other handy pre-configuration such as SSH security hardening.

Compatible with: openSUSE 13.2
						#!/bin/sh
# $Id: openSUSE_Builder.sh 764 2016-04-13 15:44:16Z archie $

# Configuration
#
# <UDF name="SS_SUSE_RELEASE" label="openSUSE Release" default="13.2" oneOf="13.2,42.1,Tumbleweed">
# <UDF name="SS_HOSTNAME" label="Hostname" example="E.g., mybox. Stored in /etc/HOSTNAME. Note openSUSE will strip off any domain part when setting the hostname(5)">
# <UDF name="SS_ENABLE_IPV6" label="Enable IPv6?" oneOf="Yes,No" default="Yes" example="Normally IPv6 is enabled, but you can disable it if you don't need/want it">
# <UDF name="SS_EXIM_ORIGIN_DOMAIN" label="Exim origin domain" default="" example="Email sent to a local user like 'jfrost' will automatically have this domain appended before routing (e.g., 'example.com' would result in 'jfrost@example.com'). Useful when you want no mail to be delivered locally. Automatically adds exim.">
# <UDF name="SS_EXIM_LOCALHOST_ONLY" label="Accept SMTP on localhost only" oneOf="Yes,No" default="Yes" example="Accept SMTP connections (port 25) on localhost only. Recommended unless you're planning to run a mail hub. Automatically adds exim.">
# <UDF name="SS_HARDEN_SSH" label="Enable additional sshd security hardening" default="Yes" oneOf="Yes,No" example="This disables password auth (i.e., allow pubkey auth only) and disables root login via SSH. You must therefore also create a sudo(8) user configured with a public key (see below) for root access over the network.">
# <UDF name="SS_LINODE_TWEAKS" label="Applies a couple of Linode recommended tweaks." oneOf="Yes,No" default="Yes" example="Sets vm.swappiness to 25 and vm.min_free_kbytes to 16384 in /etc/sysctl.conf.">
# <UDF name="SS_SUDO_USER" label="Initial sudo(8) user username" default="" example="E.g., 'jfrost'. User will be automatically created and have full sudo(8) rights">
# <UDF name="SS_SUDO_USER_REALNAME" label="Initial sudo(8) user's real name" default="" example="E.g., Jack Frost">
# <UDF name="SS_SUDO_USER_PUBKEY" label="Initial sudo(8) user's SSH public key" default="" example="Will be put into e.g., ~jfrost/.ssh/authorized_keys so user can login using SSH pubkey authentication">
# <UDF name="SS_NTP" label="Install and configure NTP daemon" default="Yes" oneOf="Yes,No" example="Will set up NTP daemon to sync from north-america.pool.ntp.org">
# <UDF name="SS_REPO_1" label="Additional repository #1" example="Either a complete URL like http://download.opensuse.org/repositories/Apache:/Modules/openSUSE_12.3/Apache:Modules.repo or an openSUSE Build Service project name like 'Apache:Modules'" default="">
# <UDF name="SS_REPO_2" label="Additional repository #2" example="URL for any zypper-supported RPM repository or an openSUSE Build Service project name" default="">
# <UDF name="SS_REPO_3" label="Additional repository #3" example="URL for any zypper-supported RPM repository or an openSUSE Build Service project name" default="">
# <UDF name="SS_REPO_4" label="Additional repository #4" example="URL for any zypper-supported RPM repository or an openSUSE Build Service project name" default="">
# <UDF name="SS_REPO_5" label="Additional repository #5" example="URL for any zypper-supported RPM repository or an openSUSE Build Service project name" default="">
# <UDF name="SS_REPO_6" label="Additional repository #6" example="URL for any zypper-supported RPM repository or an openSUSE Build Service project name" default="">
# <UDF name="SS_REPO_7" label="Additional repository #7" example="URL for any zypper-supported RPM repository or an openSUSE Build Service project name" default="">
# <UDF name="SS_REPO_8" label="Additional repository #8" example="URL for any zypper-supported RPM repository or an openSUSE Build Service project name" default="">
# <UDF name="SS_ADD_PACKAGES" label="Additional packages to install" default="wget man iputils" example="Provide RPM names here. NOTE: These RPMs must all be found in openSUSE OSS, openSUSE Non-OSS, or one of your additional repositories">
# <UDF name="SS_INSTALL_RECOMMENDS" label="Install recommended packages" default="Yes" oneOf="Yes,No" example="Installs other packages recommended by the packages you are installing">
#


# Definitions
NAME="openSUSE_Builder"
LOCKDIR="/tmp/${NAME}.lock"

# Desired openSUSE release
SUSEREL="${SS_SUSE_RELEASE}"

# Functions
showmsg()
{
    echo "**** [$$]" ${1+"$@"} >/dev/console
    sleep 2
}

addrepo()
{
    REPO=`echo ${1}`
    test -z "${REPO}" && return
    if ! echo "${REPO}" | grep -q //; then
        if expr "${SUSEREL}" \> 42 >/dev/null 2>&1; then
            RELNAME="Leap_${SUSEREL}"
        else
            RELNAME="${SUSEREL}"
        fi
        REPODIR=`echo "${REPO}" | sed -r 's|:|:/|g'`
        REPO="http://download.opensuse.org/repositories/${REPODIR}/openSUSE_${SUSEREL}/${REPO}.repo"
    fi
    showmsg Adding repository: "${REPO}"
    runcmd zypper addrepo "${REPO}"
}

sedfile()
{
    FILE="${1}"
    shift
    cp -a "${FILE}" "${FILE}.new"
    sed ${1+"$@"} < "${FILE}" > "${FILE}.new"
    mv "${FILE}.new" "${FILE}"
    showmsg Modified file: "${FILE}"
}

runcmd()
{
    echo '==> ' ${1+"$@"} >/dev/console
    ${1+"$@"} >/dev/console 2>&1
}

# Initial greeting
showmsg "openSUSE Builder for ${SUSEREL} starting"

# Work around Linode bug where two instances are started
if ! mkdir "${LOCKDIR}" 2>/dev/null; then
    showmsg "ERROR: detected concurrent openSUSE Builder, exiting (if not, rmdir ${LOCKDIR} and retry)"
    exit 1
fi

# Record input variables for debugging
set > /root/"${NAME}".vars

# Stop on any errors or signals and report them
handlefail()
{
    showmsg "ERROR: command failed: ${BASH_COMMAND}"
}

handlesignal()
{
    showmsg "ERROR: fatal signal rec'd while executing command: ${BASH_COMMAND}"
    rmdir "${LOCKDIR}" >/dev/null || :
}

set -e
trap "handlefail" ERR
trap "handlesignal" 2 3 5 10 13 15

# Clean up lock directory on exit
handleexit()
{
    rmdir "${LOCKDIR}" >/dev/null || :
}
trap "handleexit" EXIT

# Blow away old repositories
showmsg Removing old zypper repositories
rm -f /etc/zypp/repos.d/*
rm -rf /var/cache/zypp/*/*

# Setup repos for newer openSUSE
showmsg Configuring zypper with ${SUSEREL} repositories
if [ "${SUSEREL}" == 'Tumbleweed' ]; then
    runcmd zypper addrepo http://download.opensuse.org/tumbleweed/repo/oss/                         "repo-oss"
    runcmd zypper addrepo http://download.opensuse.org/tumbleweed/repo/non-oss/                     "repo-non-oss"
    runcmd zypper addrepo http://download.opensuse.org/tumbleweed/repo/debug/                       "repo-debug"
    runcmd zypper addrepo http://download.opensuse.org/update/tumbleweed/                           "repo-update"
elif expr "${SUSEREL}" \> 42 >/dev/null 2>&1; then
    runcmd zypper addrepo http://download.opensuse.org/distribution/leap/${SUSEREL}/repo/oss/       "repo-oss"
    runcmd zypper addrepo http://download.opensuse.org/distribution/leap/${SUSEREL}/repo/non-oss/   "repo-non-oss"
    runcmd zypper addrepo http://download.opensuse.org/update/leap/${SUSEREL}/oss/                  "repo-update"
    runcmd zypper addrepo http://download.opensuse.org/update/leap/${SUSEREL}/non-oss/              "repo-update-non-oss"
else
    runcmd zypper addrepo http://download.opensuse.org/distribution/${SUSEREL}/repo/oss/            "repo-oss"
    runcmd zypper addrepo http://download.opensuse.org/distribution/${SUSEREL}/repo/non-oss/        "repo-non-oss"
    runcmd zypper addrepo http://download.opensuse.org/update/${SUSEREL}/                           "repo-update"
    runcmd zypper addrepo http://download.opensuse.org/update/${SUSEREL}-non-oss/                   "repo-update-non-oss"
fi

# Update zypper itself
showmsg Refreshing update repository
runcmd zypper -n refresh -f repo-update
showmsg Updating zypper itself
yes | runcmd zypper -n install zypper libzypp 

# Configure additional repositories
addrepo "${SS_REPO_1}"
addrepo "${SS_REPO_2}"
addrepo "${SS_REPO_3}"
addrepo "${SS_REPO_4}"
addrepo "${SS_REPO_5}"
addrepo "${SS_REPO_6}"
addrepo "${SS_REPO_7}"
addrepo "${SS_REPO_8}"

# Refresh repos and accept keys
showmsg Refreshing repositories
runcmd zypper --gpg-auto-import-keys refresh

# Perform distribution upgrade: step #1 download
DUP_FLAG="--recommends"
if [ "${SS_INSTALL_RECOMMENDS}" = "No" ]; then
    DUP_FLAG="--no-recommends"
fi
showmsg Downloading new RPMs
runcmd zypper -n dist-upgrade --auto-agree-with-licenses "${DUP_FLAG}" --download-only

# Perform distribution upgrade: step #2 install
showmsg Installing new RPMs
runcmd zypper -n dist-upgrade --auto-agree-with-licenses "${DUP_FLAG}"

# We need sudo(8) if there is an initial sudo user
if [ "${SS_SUDO_USER}" != "" ]; then
    SS_ADD_PACKAGES="${SS_ADD_PACKAGES} sudo"
fi

# We need ntp(8) if SS_NTP is set
if [ "${SS_NTP}" = "Yes" ]; then
    SS_ADD_PACKAGES="${SS_ADD_PACKAGES} ntp"
fi

# We need exim(8) for exim stuff
if [ -n "${SS_EXIM_ORIGIN_DOMAIN}" -o "${SS_EXIM_LOCALHOST_ONLY}" = "Yes" ]; then
    SS_ADD_PACKAGES="${SS_ADD_PACKAGES} exim"
fi

# Install additional packages
if [ "${SS_ADD_PACKAGES}" != "" ]; then
    showmsg Installing additional RPMS: "${SS_ADD_PACKAGES}"
    runcmd zypper -n install --auto-agree-with-licenses ${SS_ADD_PACKAGES}
fi

# Set hostname
showmsg Setting hostname to "${SS_HOSTNAME}"
echo "${SS_HOSTNAME}" > /etc/HOSTNAME

# Prevent DHCP client from changing the hostname
showmsg Configuring DHCP client to not overwrite hostname
sedfile /etc/sysconfig/network/dhcp -r 's/^(DHCLIENT_SET_HOSTNAME=).*$/\1"no"/g'

# Enable/disable IPv6
if [ "${SS_ENABLE_IPV6}" = "Yes" ]; then
    showmsg Enabling IPv6
    TEMP='0'
else
    showmsg Disabling IPv6
    TEMP='1'
fi
sedfile /etc/sysctl.conf -r 's/^#?[[:space:]]*(net.ipv6.conf.all.disable_ipv6).*$/\1 = '"${TEMP}"'/g'

# Configure NTP
if [ "${SS_NTP}" = "Yes" ]; then
    showmsg Configuring NTP
    printf 'restrict default ignore\n' >> /etc/ntp.conf
    printf 'restrict -6 default ignore\n' >> /etc/ntp.conf
    for i in 1 2 3 4; do
        printf 'server   time%s.google.com\n' "${i}" >> /etc/ntp.conf
        printf 'restrict time%s.google.com kod nomodify notrap nopeer noquery\n' "${i}" >> /etc/ntp.conf
    done
    printf 'restrict 127.0.0.1\n' >> /etc/ntp.conf
    printf 'restrict -6 ::1 \n' >> /etc/ntp.conf
    test -e /etc/sysconfig/ntp.orig || cp -a /etc/sysconfig/ntp /etc/sysconfig/ntp.orig
    sedfile /etc/sysconfig/ntp -r 's/^(NTPD_OPTIONS=).*$/\1"-g -u ntp:ntp"/g'
    systemctl enable ntpd.service
fi

# Configure exim 'origin domain'
if [ -n "${SS_EXIM_ORIGIN_DOMAIN}" ]; then
    if [ -f /etc/exim/exim.conf ]; then
        showmsg "Configuring exim with origin domain ${SS_EXIM_ORIGIN_DOMAIN}"
        test -e /etc/exim/exim.conf.orig || cp -a /etc/exim/exim.conf /etc/exim/exim.conf.orig
        sedfile /etc/exim/exim.conf -r 's/^#?[[:space:]]*(qualify_domain[[:space:]]*=).*$/\1 '"${SS_EXIM_ORIGIN_DOMAIN}"'/g'
    else
        showmsg "Didn't find exim - can't configure for origin domain"
    fi
fi

# Configure exim local_interfaces
if [ "${SS_EXIM_LOCALHOST_ONLY}" = "Yes" ]; then
    if [ -f /etc/exim/exim.conf ]; then
        showmsg "Configuring exim to only accept connections on localhost"
        test -e /etc/exim/exim.conf.orig || cp -a /etc/exim/exim.conf /etc/exim/exim.conf.orig
        sedfile /etc/exim/exim.conf -r '/^#?[[:space:]]*(qualify_domain[[:space:]]*=)/alocal_interfaces = 127.0.0.1'
    else
        showmsg "Didn't find exim - can't configure for localhost only"
    fi
fi

# Optionally create sudo(8) user
if [ "${SS_SUDO_USER}" != "" ]; then

    # Create user
    showmsg Creating user "${SS_SUDO_USER}"
    runcmd useradd -p '*' -c "${SS_SUDO_USER_REALNAME}" -m -s /bin/bash "${SS_SUDO_USER}"

    # Give user full sudo rights
    showmsg Giving user "${SS_SUDO_USER}" full 'sudo(8)' rights
    printf "%s\tALL=(ALL) NOPASSWD: ALL\n" "${SS_SUDO_USER}" >> /etc/sudoers

    # Optionally configure SSH pubkey
    if [ "${SS_SUDO_USER_PUBKEY}" != "" ]; then
        showmsg Configuring SSH public key 'for' user "${SS_SUDO_USER}"
        mkdir -m 0755 /home/"${SS_SUDO_USER}"/.ssh
        echo "${SS_SUDO_USER_PUBKEY}" > /home/"${SS_SUDO_USER}"/.ssh/authorized_keys
        chown -R "${SS_SUDO_USER}":users /home/"${SS_SUDO_USER}"/.ssh
    fi
fi

# Optionally harden SSH
if [ "${SS_HARDEN_SSH}" = "Yes" ]; then
    showmsg Security hardening SSHD configuration
    test -e /etc/ssh/sshd_config.orig || cp -a /etc/ssh/sshd_config /etc/ssh/sshd_config.orig
    sedfile /etc/ssh/sshd_config -r \
      -e 's/^#?(PermitRootLogin ).*$/\1no/g' \
      -e 's/^#?(PasswordAuthentication ).*$/\1no/g' \
      -e 's/^#?(ChallengeResponseAuthentication ).*$/\1no/g' \
      -e 's/^#?(UsePAM ).*$/\1no/g'
fi

# Disable graphics
showmsg Setting multi-user mode as default '(i.e., disabling graphical mode)'
systemctl set-default multi-user.target

# Additional sysctl tweaks suggested by Linode
if [ "${SS_LINODE_TWEAKS}" = "Yes" ]; then
    showmsg Adding some recommended tweaks to /etc/sysctl.conf
    cat >> /etc/sysctl.conf << xxEOFxx

# Suggested by http://www.linode.com/wiki/index.php/Swappiness
vm.swappiness=25

# Suggested by Linode to help with low memory situations
vm.min_free_kbytes=16384
xxEOFxx
fi

# Done
showmsg "Installation done... reboot your system for changes to take effect!"