From f7ca8d84e8b7346f273bcd0b3622d9ed66ab268a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 8 Mar 2018 17:53:23 +0000 Subject: [PATCH] Add yunohost installation as stage3 --- stage3/00-install-yunohost/00-run.sh | 8 + .../files/install_yunohost | 520 ++++++++++++++++++ 2 files changed, 528 insertions(+) create mode 100755 stage3/00-install-yunohost/00-run.sh create mode 100644 stage3/00-install-yunohost/files/install_yunohost diff --git a/stage3/00-install-yunohost/00-run.sh b/stage3/00-install-yunohost/00-run.sh new file mode 100755 index 0000000..fa2521d --- /dev/null +++ b/stage3/00-install-yunohost/00-run.sh @@ -0,0 +1,8 @@ +#!/bin/bash -e + +install -m 744 files/install_yunohost ${ROOTFS_DIR}/tmp/ + +on_chroot << EOF +cd /tmp/ +./install_yunohost -a -i +EOF diff --git a/stage3/00-install-yunohost/files/install_yunohost b/stage3/00-install-yunohost/files/install_yunohost new file mode 100644 index 0000000..754aa43 --- /dev/null +++ b/stage3/00-install-yunohost/files/install_yunohost @@ -0,0 +1,520 @@ +#!/bin/bash + +# Copyright (C) 2015-2017 YunoHost +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +set -u + +# Globals + +readonly YUNOHOST_LOG="/var/log/yunohost-installation_$(date +%Y%m%d_%H%M%S).log" + +############################################################################### +# Main functions # +############################################################################### + +function usage() { + echo " +Usage : + `basename $0` [-a] [-d ] [-h] + +Options : + -a Enable automatic mode. No questions are asked. + This does not perform the post-install step. + -d Choose the distribution to install ('stable', 'testing', 'unstable'). + Defaults to 'stable' + -f Ignore checks before starting the installation. Use only if you know + what you are doing. + -h Prints this help and exit +" +} + +function parse_options() +{ + AUTOMODE=0 + DISTRIB=stable + BUILD_IMAGE=0 + FORCE=0 + + while getopts ":aid:fh" option; do + case $option in + a) + AUTOMODE=1 + export DEBIAN_FRONTEND=noninteractive + ;; + d) + DISTRIB=$OPTARG + ;; + f) + FORCE=1 + ;; + i) + # This hidden option will allow to build generic image for Rpi/Olimex + BUILD_IMAGE=1 + ;; + h) + usage + exit 0 + ;; + :) + usage + exit 1 + ;; + \?) + usage + exit 1 + ;; + esac + done +} + +function main() +{ + parse_options "$@" + + check_assertions + + step upgrade_system || die "Unable to update the system" + step install_script_dependencies || die "Unable to install dependencies to install script" + step create_custom_config || die "Creating custom configuration file /etc/yunohost/yunohost.conf failed" + step confirm_installation || die "Installation cancelled at your request" + step setup_package_source || die "Setting up deb package sources failed" + step apt_update || die "Error caught during 'apt-get update'" + step register_debconf || die "Unable to insert new values into debconf database" + step workaround_avahi_installation || die "Unable to install workaround for avahi installation" + step install_yunohost_packages || die "Installation of Yunohost packages failed" + step restart_services || die "Error caught during services restart" + + if is_raspbian ; then + step del_user_pi || die "Unable to delete user pi" + step setup_firstboot || die "Unable to setup firstboot" + fi + + if [[ "$BUILD_IMAGE" == "1" ]] ; then + step clean_image || die "Unable to clean image" + fi + + if is_raspbian ; then + # Reboot should be done before postinstall to be able to run iptables rules + reboot + fi + + step post_install || die "Post-installation failed" + + info "Installation logs are available in $YUNOHOST_LOG" + success "YunoHost installation completed !" + exit 0 +} + +############################################################################### +# Helpers # +############################################################################### + +readonly normal=$(printf '\033[0m') +readonly bold=$(printf '\033[1m') +readonly faint=$(printf '\033[2m') +readonly underline=$(printf '\033[4m') +readonly negative=$(printf '\033[7m') +readonly red=$(printf '\033[31m') +readonly green=$(printf '\033[32m') +readonly orange=$(printf '\033[33m') +readonly blue=$(printf '\033[34m') +readonly yellow=$(printf '\033[93m') +readonly white=$(printf '\033[39m') + +function success() +{ + local msg=${1} + echo "[${bold}${green} OK ${normal}] ${msg}" | tee -a $YUNOHOST_LOG +} + +function info() +{ + local msg=${1} + echo "[${bold}${blue}INFO${normal}] ${msg}" | tee -a $YUNOHOST_LOG +} + +function warn() +{ + local msg=${1} + echo "[${bold}${orange}WARN${normal}] ${msg}" | tee -a $YUNOHOST_LOG >&2 +} + +function error() +{ + local msg=${1} + echo "[${bold}${red}FAIL${normal}] ${msg}" | tee -a $YUNOHOST_LOG >&2 +} + +function die() { + error "$1" + info "Installation logs are available in $YUNOHOST_LOG" + exit 1 +} + +function step() { + info "Running $1" + $* + local return_code="$?" + return $return_code +} + +function apt_get_wrapper() { + if [[ "$AUTOMODE" == "0" ]] ; + then + debconf-apt-progress \ + --logfile $YUNOHOST_LOG \ + -- \ + apt-get $* + else + apt-get $* >> $YUNOHOST_LOG 2>&1 + fi +} + + +function apt_update() { + apt_get_wrapper update +} + +############################################################################### +# Installation steps # +############################################################################### + +function check_assertions() +{ + # Assert we're on Debian + # Note : we do not rely on lsb_release to avoid installing a dependency + # only to check this... + [[ -f "/etc/debian_version" ]] || die "This script can only be ran on Debian." + + # Assert we're on Stretch + # Note : we do not rely on lsb_release to avoid installing a dependency + # only to check this... + [[ "$(cat /etc/debian_version)" =~ ^9.* ]] || die "This script can only be ran on Debian Stretch." + + # Assert we're root + [[ "$(id -u)" == "0" ]] || die "This script must be run as root." + + # Assert systemd is installed + command -v systemctl > /dev/null || die "YunoHost requires systemd to be installed." + + # If we're on Raspbian, we want the user 'pi' to be logged out because + # it's going to be deleted for security reasons... + if is_raspbian ; then + user_pi_logged_out || die "The user pi should be logged out." + fi + + # Check possible conflict with apache, bind9. + [[ -z "$(dpkg --get-selections | grep -v deinstall | grep 'bind9 ')" ]] || [[ "$FORCE" == "1" ]] \ + || die "Bind9 is installed and might conflict with dnsmasq. Uninstall it first, or if you know what you are doing, run this script with -f." + + [[ -z "$(dpkg --get-selections | grep -v deinstall | grep 'apache2 ')" ]] || [[ "$FORCE" == "1" ]] \ + || die "Apache is installed and might conflict with nginx. Uninstall it first, or if you know what you are doing, run this script with -f." + +} + +function upgrade_system() { + + apt_get_wrapper update \ + || return 1 + + apt_get_wrapper -y dist-upgrade \ + || return 2 + + if is_raspbian ; then + apt_get_wrapper -o Dpkg::Options::="--force-confold" \ + -y --force-yes install rpi-update \ + || return 3 + + if [[ "$BUILD_IMAGE" != "1" ]] ; then + rpi-update >> $YUNOHOST_LOG 2>&1 \ + || return 4 + fi + fi +} + +function install_script_dependencies() { + # dependencies of the install script itself + local DEPENDENCIES="lsb-release wget whiptail" + + if [[ "$AUTOMODE" == "0" ]] ; + then + DEPENDENCIES+=" dialog" + fi + + apt_get_wrapper -o Dpkg::Options::="--force-confold" \ + -y --force-yes install \ + $DEPENDENCIES \ + || return 1 +} + +function create_custom_config() { + # Create YunoHost configuration folder + mkdir -p /etc/yunohost/ + + # Store info about installation method + touch /etc/yunohost/from_script +} + +function confirm_installation() { + [[ "$AUTOMODE" == "1" ]] && return 0 + + local text=" +Caution ! + +Your configuration files for : + - postfix + - dovecot + - mysql + - nginx + - metronome +will be overwritten ! + +Are you sure you want to proceed with the installation of Yunohost? +" + whiptail --title "Yunohost Installation" --yesno "$text" 20 78 +} + +function setup_package_source() { + + local CUSTOMAPT=/etc/apt/sources.list.d/yunohost.list + + # Debian repository + + # FIXME : move "vinaigrette." to "repo." at some point... + local CUSTOMDEB="deb http://vinaigrette.yunohost.org/debian/ stretch stable testing unstable" + + if [[ "$DISTRIB" == "stable" ]] ; then + echo "$CUSTOMDEB" > $CUSTOMAPT + elif [[ "$DISTRIB" == "testing" ]] ; then + echo "$CUSTOMDEB testing" > $CUSTOMAPT + elif [[ "$DISTRIB" == "unstable" ]] ; then + echo "$CUSTOMDEB testing unstable" > $CUSTOMAPT + fi + + # Add YunoHost repository key to the keyring + # FIXME : move "vinaigrette." to "repo." at some point... + wget -O- https://vinaigrette.yunohost.org/yunohost.asc -q | apt-key add -qq - >/dev/null 2>&1 +} + +function register_debconf() { + debconf-set-selections << EOF +slapd slapd/password1 password yunohost +slapd slapd/password2 password yunohost +slapd slapd/domain string yunohost.org +slapd shared/organization string yunohost.org +slapd slapd/allow_ldap_v2 boolean false +slapd slapd/invalid_config boolean true +slapd slapd/backend select MDB +postfix postfix/main_mailer_type select Internet Site +postfix postfix/mailname string /etc/mailname +mysql-server-5.5 mysql-server/root_password password yunohost +mysql-server-5.5 mysql-server/root_password_again password yunohost +mariadb-server-10.0 mysql-server/root_password password yunohost +mariadb-server-10.0 mysql-server/root_password_again password yunohost +nslcd nslcd/ldap-bindpw password +nslcd nslcd/ldap-starttls boolean false +nslcd nslcd/ldap-reqcert select +nslcd nslcd/ldap-uris string ldap://localhost/ +nslcd nslcd/ldap-binddn string +nslcd nslcd/ldap-base string dc=yunohost,dc=org +libnss-ldapd libnss-ldapd/nsswitch multiselect group, passwd, shadow +EOF +} + +function workaround_avahi_installation() { + + # When attempting several installation of Yunohost on the same host + # with a light VM system like LXC + # we hit a bug with avahi-daemon postinstallation + # This is described in detail in https://github.com/lxc/lxc/issues/25 + # + # It makes the configure step of avahi-daemon fail, because the service does + # start correctly. Then all other packages depending on avahi-daemon refuse to + # configure themselves. + # + # The workaround we use is to generate a random uid for the avahi user, and + # create the user with this id beforehand, so that the avahi-daemon postinst + # script does not do it on its own. Our randomized uid has far less chances to + # be already in use in another system than the automated one (which tries to use + # consecutive uids). + + # Return without error if avahi already exists + if id avahi > /dev/null 2>&1 ; then + info "User avahi already exists (with uid $(id avahi)), skipping avahi workaround" + return 0 + fi + + # Get a random unused uid between 500 and 999 (system-user) + local avahi_id=$((500 + RANDOM % 500)) + while cut -d ':' -f 3 /etc/passwd | grep -q $avahi_id ; + do + avahi_id=$((500 + RANDOM % 500)) + done + + info "Workaround for avahi : creating avahi user with uid $avahi_id" + + # Use the same adduser parameter as in the avahi-daemon postinst script + # Just specify --uid explicitely + adduser --disabled-password --quiet --system \ + --home /var/run/avahi-daemon --no-create-home \ + --gecos "Avahi mDNS daemon" --group avahi \ + --uid $avahi_id +} + +function install_yunohost_packages() { + # Allow sudo removal even if no root password has been set (on some DO + # droplet or Vagrant virtual machines), as YunoHost use sudo-ldap + export SUDO_FORCE_REMOVE=yes + + # On some machines (e.g. OVH VPS), the /etc/resolv.conf is immutable + # We need to make it mutable for the resolvconf dependency to be installed + chattr -i /etc/resolv.conf 2>/dev/null || true + + # Install YunoHost + apt_get_wrapper \ + -o Dpkg::Options::="--force-confold" \ + -y --force-yes install \ + yunohost yunohost-admin postfix +} + +function restart_services() { + service slapd restart +# service yunohost-firewall start + service unscd restart + service nslcd restart + + # NOTE : We don't fail if slapd fails to restart... + return 0 +} + +function post_install() { + # No postinstall in auto mode + [[ "$AUTOMODE" == "1" ]] && return 0 + + # Remove whiptail and dialog remains... + clear + + local text=" +Yunohost packages have been installed successfully! + +You can now proceed with Yunohost post-installation. +This is where you will be asked for : +- the main DNS domain name of your server +- the administration password + +You can also perform this step later on your own : +- either from a shell, by running 'yunohost tools postinstall' + as root +- either from your web browser, by accessing https://yunohost.local + +Please refer to https://yunohost.org/#/postinstall +for additionnal information. + +Do you want to proceed with YunoHost post-installation now? +" + whiptail --title "Post-installation" --yesno "$text" 25 78 \ + || return 0 + + /usr/bin/yunohost tools postinstall + + local POSTINSTALL_EXIT_CODE="$?" + while [[ "$POSTINSTALL_EXIT_CODE" != "0" ]] ; + do + local text_retry=" +Yunohost post-installation has failed. + +Do you want to try again now? +" + whiptail --title "Post-installation" --yesno "$text_retry" 12 78 --defaultno \ + || return $POSTINSTALL_EXIT_CODE + + /usr/bin/yunohost tools postinstall + POSTINSTALL_EXIT_CODE="$?" + done + return 0 +} + +############################################################################### +# Raspbian specific stuff # +############################################################################### + +function is_raspbian() { + # On Raspbian image lsb_release is available + if [[ "$(lsb_release -i -s 2> /dev/null)" != "Raspbian" ]] ; + then + return 1 + fi + return 0 +} + +function user_pi_logged_out() { + who | grep pi > /dev/null && return 1 + return 0 +} + +function del_user_pi() { + deluser --remove-all-files pi >> $YUNOHOST_LOG 2>&1 +} + +function setup_firstboot() { + + cat > /etc/init.d/yunohost-firstboot << EOF +#!/bin/sh +### BEGIN INIT INFO +# Provides: expand rootfs and Generates new ssh host keys on first boot +# Required-Start: \$remote_fs \$syslog +# Required-Stop: \$remote_fs \$syslog +# Default-Start: 2 3 4 5 +# Default-Stop: +# Short-Description: Generates new ssh host keys on first boot +# Description: Generates apt-get --purge clean new ssh host keys on $ +### END INIT INFO +echo "Expanding rootfs ..." +raspi-config --expand-rootfs +echo "Removing myself ..." +insserv -r /etc/init.d/yunohost-firstboot +rm -f /etc/init.d/yunohost-firstboot +rm /etc/yunohost/firstboot +echo "Rebooting ..." +reboot +EOF + chmod a+x /etc/init.d/yunohost-firstboot + insserv /etc/init.d/yunohost-firstboot + touch /etc/yunohost/firstboot +} + +############################################################################### +# Image building specific stuff # +############################################################################### + +function clean_image() { + # Delete SSH keys + rm -f /etc/ssh/ssh_host_* >> $YUNOHOST_LOG 2>&1 + yes | ssh-keygen -f /etc/ssh/ssh_host_rsa_key -N '' -t rsa >> $YUNOHOST_LOG 2>&1 + yes | ssh-keygen -f /etc/ssh/ssh_host_dsa_key -N '' -t dsa >> $YUNOHOST_LOG 2>&1 + yes | ssh-keygen -f /etc/ssh/ssh_host_ecdsa_key -N '' -t ecdsa -b 521 >> $YUNOHOST_LOG 2>&1 + + # Deleting logs ... + find /var/log -type f -exec rm {} \; >> $YUNOHOST_LOG 2>&1 + + # Purging apt ... + apt-get --purge clean >> $YUNOHOST_LOG 2>&1 +} + + +############################################################################### + +main "$@"