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.
#!/bin/sh # $Id: openSUSE_Builder.sh 908 2017-11-28 20:46:31Z archie $ # Configuration # # <UDF name="SS_SUSE_RELEASE" label="openSUSE Release" default="42.3" oneOf="42.2,42.3,Tumbleweed"> # <UDF name="SS_HOSTNAME" label="Hostname" default="" 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="No" 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_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="No" oneOf="Yes,No" example="Will set up NTP daemon to sync from timeX.google.com"> # <UDF name="SS_REPO_1" label="Additional repository #1" example="Either a complete URL like http://download.opensuse.org/repositories/Apache:/Modules/openSUSE_Leap_42.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_ADD_PACKAGES" label="Additional packages to install" default="man-pages 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" else 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" 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}" # 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}" # Make "required only" permanent if configured if [ "${SS_INSTALL_RECOMMENDS}" = "No" ]; then sedfile /etc/zypp/zypp.conf -r 's/^(#[[:space:]]*)?(solver.onlyRequires = ).*$/\2true/g' fi # 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 if [ -n "${SS_HOSTNAME}" ]; then 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/^(DHCLIENT6?_SET_HOSTNAME=).*$/\1"no"/g' fi # Enable/disable IPv6 if [ "${SS_ENABLE_IPV6}" = "No" ]; then showmsg Disabling IPv6 sedfile /etc/sysctl.conf -r 's/^#?[[:space:]]*(net.ipv6.conf.all.disable_ipv6).*$/\1 = '"${TEMP}"'/g' fi # 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 printf 'restrict 127.0.0.1\n' >> /etc/ntp.conf printf 'restrict -6 ::1 \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 nomodify notrap nopeer noquery\n' "${i}" >> /etc/ntp.conf done test -e /etc/sysconfig/ntp.orig || cp -a /etc/sysconfig/ntp /etc/sysconfig/ntp.orig 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 # Done showmsg "Installation done... reboot your system for changes to take effect!"