diff --git a/scripts/_common.sh b/scripts/_common.sh new file mode 100644 index 0000000..3b80ce9 --- /dev/null +++ b/scripts/_common.sh @@ -0,0 +1,92 @@ + +#================================================= +# COMMON VARIABLES +#================================================= + +pkg_dependencies="vim build-essential git zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev python-docutils pkg-config cmake nodejs graphviz ruby2.5 bundler default-libmysqlclient-dev jq python-requests bsdmainutils" + +---------------- + + +YNH_PHP_VERSION="7.3" +extra_php_dependencies="php${YNH_PHP_VERSION}-bz2 php${YNH_PHP_VERSION}-imap php${YNH_PHP_VERSION}-smbclient php${YNH_PHP_VERSION}-gmp php${YNH_PHP_VERSION}-gd php${YNH_PHP_VERSION}-json php${YNH_PHP_VERSION}-intl php${YNH_PHP_VERSION}-curl php${YNH_PHP_VERSION}-apcu php${YNH_PHP_VERSION}-redis php${YNH_PHP_VERSION}-ldap php${YNH_PHP_VERSION}-imagick php${YNH_PHP_VERSION}-zip php${YNH_PHP_VERSION}-mbstring php${YNH_PHP_VERSION}-xml php${YNH_PHP_VERSION}-mysql php${YNH_PHP_VERSION}-igbinary php${YNH_PHP_VERSION}-bcmath" + +#================================================= +# EXPERIMENTAL HELPERS +#================================================= + +# Check if an URL is already handled +# usage: is_url_handled --domain=DOMAIN --path=PATH_URI +is_url_handled() { + # Declare an array to define the options of this helper. + local legacy_args=dp + declare -Ar args_array=( [d]=domain= [p]=path= ) + local domain + local path + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + # Try to get the url with curl, and keep the http code and an eventual redirection url. + local curl_output="$(curl --insecure --silent --output /dev/null \ + --write-out '%{http_code};%{redirect_url}' https://127.0.0.1$path --header "Host: $domain" --resolve $domain:443:127.0.0.1)" + + # Cut the output and keep only the first part to keep the http code + local http_code="${curl_output%%;*}" + # Do the same thing but keep the second part, the redirection url + local redirection="${curl_output#*;}" + + # Return 1 if the url isn't handled. + # Which means either curl got a 404 (or the admin) or the sso. + # A handled url should redirect to a publicly accessible url. + # Return 1 if the url has returned 404 + if [ "$http_code" = "404" ] || [[ $redirection =~ "/yunohost/admin" ]]; then + return 1 + # Return 1 if the url is redirected to the SSO + elif [[ $redirection =~ "/yunohost/sso" ]]; then + return 1 + fi +} + + +#================================================= + +# Check available space before creating a temp directory. +# +# usage: ynh_smart_mktemp --min_size="Min size" +# +# | arg: -s, --min_size= - Minimal size needed for the temporary directory, in Mb +ynh_smart_mktemp () { + # Declare an array to define the options of this helper. + declare -Ar args_array=( [s]=min_size= ) + local min_size + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + min_size="${min_size:-300}" + # Transform the minimum size from megabytes to kilobytes + min_size=$(( $min_size * 1024 )) + + # Check if there's enough free space in a directory + is_there_enough_space () { + local free_space=$(df --output=avail "$1" | sed 1d) + test $free_space -ge $min_size + } + + if is_there_enough_space /tmp; then + local tmpdir=/tmp + elif is_there_enough_space /var; then + local tmpdir=/var + elif is_there_enough_space /; then + local tmpdir=/ + elif is_there_enough_space /home; then + local tmpdir=/home + else + ynh_die "Insufficient free space to continue..." + fi + + echo "$(mktemp --directory --tmpdir="$tmpdir")" +} + +#================================================= +# FUTURE OFFICIAL HELPERS +#================================================= diff --git a/scripts/install b/scripts/install index 82a4471..7a6fc4c 100755 --- a/scripts/install +++ b/scripts/install @@ -1,118 +1,139 @@ #!/bin/bash + old_pwd=$(pwd) -APP=huginn +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source _common.sh +source /usr/share/yunohost/helpers + +#================================================= +# MANAGE SCRIPT FAILURE +#================================================= + +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors + +#================================================= +# RETRIEVE ARGUMENTS FROM THE MANIFEST +#================================================= + +domain=$YNH_APP_ARG_DOMAIN +path=$YNH_APP_ARG_PATH +admin=$YNH_APP_ARG_ADMIN +admin_pwd=$YNH_APP_ARG_PASSWORD +invitation=$YNH_APP_ARG_INVITATION +is_public=$YNH_APP_ARG_IS_PUBLIC + +app=$YNH_APP_INSTANCE_NAME + +#================================================= +# CHECK IF THE APP CAN BE INSTALLED WITH THESE ARGS +#================================================= +ynh_script_progression --message="Validating installation parameters..." + +final_path=/var/www/$app +test ! -e "$final_path" || ynh_die --message="This path already contains a folder" + +# Register (book) web path +ynh_webpath_register --app=$app --domain=$domain --path_url=$path_url + +#================================================= +# STORE SETTINGS FROM MANIFEST +#================================================= +ynh_script_progression --message="Storing installation settings..." + +ynh_app_setting_set --app=$app --key=domain --value=$domain +ynh_app_setting_set --app=$app --key=path --value=$path +ynh_app_setting_set --app=$app --key=admin --value=$admin +ynh_app_setting_set --app=$app --key=admin_pwd --value=$admin_pwd +ynh_app_setting_set --app=$app --key=invitation --value=$invitation +ynh_app_setting_set --app=$app --key=is_public --value=$is_public + +#================================================= +# STANDARD MODIFICATIONS +#================================================= +# INSTALL DEPENDENCIES +#================================================= +ynh_script_progression --message="Installing dependencies..." --weight=10 -# Retrieve arguments -domain=$1 -path=$2 -admin=$3 -admin_pwd=$4 -invitation=$5 -is_public=$6 +#Import node.js repository (can be skipped on Ubuntu and Debian Jessie): +# curl -sL https://deb.nodesource.com/setup_0.12 | sudo bash - +ynh_install_app_dependencies $pkg_dependencies -# Save APP settings -sudo yunohost app setting $APP admin -v "$admin" -sudo yunohost app setting $APP is_public -v "$is_public" +# Install the bundler and foreman gems: -# Check domain/path availability -sudo yunohost app checkurl $domain$path -a $APP -if [[ ! $? -eq 0 ]]; then - exit 1 -fi +gem install rake foreman --no-document +gem install bundler -v '< 2' --no-document -#Import node.js repository (can be skipped on Ubuntu and Debian Jessie): -curl -sL https://deb.nodesource.com/setup_0.12 | sudo bash - - -# Install dependencies -sudo apt-get update -qq -sudo apt-get install -y runit build-essential git zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate python-docutils pkg-config cmake nodejs graphviz -qq - -#Remove the old Ruby versions if present: -sudo apt-get remove -y ruby1.8 ruby1.9 -qq - -#Download Ruby and compile it: - - mkdir /tmp/ruby && cd /tmp/ruby - curl -L --silent http://cache.ruby-lang.org/pub/ruby/2.2/ruby-2.2.3.tar.bz2 | tar xj - cd ruby-2.2.3 - ./configure --disable-install-rdoc > /dev/null - make -j -s `nproc` - sudo make install - -#Install the bundler and foreman gems: - sudo gem install bundler foreman --no-ri --no-rdoc - -#Create a user for Huginn: - sudo adduser --disabled-login --gecos 'Huginn' huginn - -#Install the database packages - sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev -qq - -# Initialize database and store mysql password for upgrade -db_pwd=$(sudo yunohost app initdb huginn -d huginn_production) -sudo yunohost app setting huginn mysqlpwd -v $db_pwd - -# Delete db and user if exit with an error -function exit_properly - { - set +e - root_pwd=$(sudo cat /etc/yunohost/mysql) - mysql -u root -p$root_pwd -e "DROP DATABASE huginn_production ; DROP USER $APP@localhost ;" - - sudo userdel $APP - sudo rm -rf /home/huginn - - exit 1 -} -trap exit_properly ERR +# Update rubygems: + +gem update --system --no-document + +#================================================= +# CREATE DEDICATED USER +#================================================= +ynh_script_progression --message="Configuring system user..." --weight=3 + +# Create a system user +ynh_system_user_create --username=$app --home_dir=/home/huginn +#================================================= +# CREATE A MYSQL DATABASE +#================================================= +ynh_script_progression --message="Creating a MySQL database..." --weight=2 + +ynh_app_setting_set --app=$app --key=db_name --value=huginn +ynh_mysql_setup_db --db_user=huginn --db_name=huginn # We'll install Huginn into the home directory of the user "huginn" - cd /home/huginn +cd /home/huginn - # Clone Huginn repository - sudo sudo -u huginn -H git clone https://github.com/cantino/huginn.git -b master huginn +# Clone Huginn repository +sudo sudo -u huginn -H git clone https://github.com/cantino/huginn.git -b master huginn - # Go to Huginn installation folder - cd /home/huginn/huginn +# Go to Huginn installation folder +cd /home/huginn/huginn - # Copy the example Huginn config - sudo sudo -u huginn -H cp .env.example .env +# Copy the example Huginn config +sudo sudo -u huginn -H cp .env.example .env - # Create the log/, tmp/pids/ and tmp/sockets/ directories - sudo sudo -u huginn mkdir -p log tmp/pids tmp/sockets +# Create the log/, tmp/pids/ and tmp/sockets/ directories +sudo sudo -u huginn mkdir -p log tmp/pids tmp/sockets - # Make sure Huginn can write to the log/ and tmp/ directories - sudo chown -R huginn log/ tmp/ - sudo chmod -R u+rwX,go-w log/ tmp/ +# Make sure Huginn can write to the log/ and tmp/ directories +sudo chown -R huginn log/ tmp/ +sudo chmod -R u+rwX,go-w log/ tmp/ - # Make sure permissions are set correctly - sudo chmod -R u+rwX,go-w log/ - sudo chmod -R u+rwX tmp/ - sudo sudo -u huginn -H chmod o-rwx .env +# Make sure permissions are set correctly +sudo chmod -R u+rwX,go-w log/ +sudo chmod -R u+rwX tmp/ +sudo sudo -u huginn -H chmod o-rwx .env - # Copy the example Unicorn config - sudo sudo -u huginn -H cp config/unicorn.rb.example config/unicorn.rb +# Copy the example Unicorn config +sudo sudo -u huginn -H cp config/unicorn.rb.example config/unicorn.rb - #Install Gems - sudo sudo -u huginn -H bundle install --deployment --without development test +#Install Gems +sudo sudo -u huginn -H bundle install --deployment --without development test #rake secret RAKE_SECRET=$(sudo sudo -u huginn -H rake secret) #Edit .env - sudo sudo -u huginn -H sed -i "s/\(DATABASE_PASSWORD *= *\).*/\1\"$db_pwd\"/" .env - sudo sudo -u huginn -H sed -i "s/\(DATABASE_USERNAME *= *\).*/\1\"huginn\"/" .env - sudo sudo -u huginn -H sed -i "s/\(DATABASE_NAME *= *\).*/\1huginn_production/" .env - sudo sudo -u huginn -H sed -i "s/\(APP_SECRET_TOKEN *= *\).*/\1$RAKE_SECRET/" .env - sudo sudo -u huginn -H sed -i "s/\(INVITATION_CODE *= *\).*/\1$invitation/" .env - - #uncomment RAILS_ENV - sudo sudo -u huginn -H sed -i '/# RAILS_ENV=production/s/^# //' .env +sudo sudo -u huginn -H sed -i "s/\(DATABASE_PASSWORD *= *\).*/\1\"$db_pwd\"/" .env +sudo sudo -u huginn -H sed -i "s/\(DATABASE_USERNAME *= *\).*/\1\"huginn\"/" .env +sudo sudo -u huginn -H sed -i "s/\(DATABASE_NAME *= *\).*/\1huginn/" .env +sudo sudo -u huginn -H sed -i "s/\(APP_SECRET_TOKEN *= *\).*/\1$RAKE_SECRET/" .env +sudo sudo -u huginn -H sed -i "s/\(INVITATION_CODE *= *\).*/\1$invitation/" .env +#uncomment RAILS_ENV +sudo sudo -u huginn -H sed -i '/# RAILS_ENV=production/s/^# //' .env # Create the database sudo sudo -u huginn -H bundle exec rake db:create RAILS_ENV=production @@ -134,8 +155,16 @@ cd /home/huginn/huginn/ #Export the init scripts: sudo rake production:export -### Setup Logrotate -sudo cp deployment/logrotate/huginn /etc/logrotate.d/huginn +#================================================= +# SETUP LOGROTATE +#================================================= +ynh_script_progression --message="Configuring log rotation..." + +# Use logrotate to manage application logfile(s) +ynh_use_logrotate --logfile="$datadir/huginn.log" + + + # Modify Nginx configuration file and copy it to Nginx conf directory cd $old_pwd @@ -160,3 +189,172 @@ sudo service nginx reload sudo yunohost app ssowatconf +--------------- + + + + +#================================================= +# DOWNLOAD, CHECK AND UNPACK SOURCE +#================================================= +ynh_script_progression --message="Setting up source files..." + +# Load the last available version +source upgrade.d/upgrade.last.sh + +# Create an app.src for the last version of nextcloud +cat > ../conf/app.src << EOF +SOURCE_URL=https://download.nextcloud.com/server/releases/nextcloud-$next_version.tar.bz2 +SOURCE_SUM=$nextcloud_source_sha256 +SOURCE_SUM_PRG=sha256sum +SOURCE_FORMAT=tar.bz2 +SOURCE_IN_SUBDIR=true +EOF + +ynh_app_setting_set --app=$app --key=final_path --value=$final_path +# Enable YunoHost patches on Nextcloud sources +cp -a ../sources/patches_last_version/* ../sources/patches +# Download, check integrity, uncompress and patch the source from app.src +ynh_setup_source --dest_dir="$final_path" + + +#================================================= +# PHP-FPM CONFIGURATION +#================================================= +ynh_script_progression --message="Configuring PHP-FPM..." --weight=50 + +# Create a dedicated php-fpm config +ynh_add_fpm_config --usage=medium --footprint=high --phpversion=$YNH_PHP_VERSION --package="$extra_php_dependencies" +# Used by ynh_add_nginx_config +phpversion=$(ynh_app_setting_get --app=$app --key=phpversion) + +#================================================= +# NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Configuring NGINX web server..." --weight=2 + +# Check if .well-known is available for this domain +if is_url_handled --domain="$domain" --path="/.well-known/caldav" || is_url_handled --domain="$domain" --path="/.well-known/carddav" +then + ynh_print_warn --message="Another app already uses the domain $domain to serve a caldav/carddav feature. You may encounter issues when dealing with your calendar or address book." + + # Remove lines about .well-known/carddav and caldav with sed. + sed --in-place --regexp-extended '/location = \/\.well\-known\/(caldav|carddav)/d' "../conf/nginx.conf" +fi + +# Create a dedicated NGINX config +ynh_add_nginx_config + +#================================================= +# SPECIFIC SETUP +#================================================= +# INSTALL NEXTCLOUD +#================================================= +ynh_script_progression --message="Installing Nextcloud..." --weight=30 + +# Define a function to execute commands with `occ` +exec_occ() { + (cd "$final_path" && ynh_exec_as "$app" \ + php${phpversion} occ --no-interaction --no-ansi "$@") +} + +# Set write access for the following commands +chown -R $app: "$final_path" "$datadir" + +# Define password in an intermediate var +# The fact that it's called _password allows it to be +# picked up by Yunohost's auto-redact mecanism +admin_password="$(ynh_string_random --length=6)" + +# Install Nextcloud using a temporary admin user +exec_occ maintenance:install \ + --database "mysql" --database-name $db_name \ + --database-user $db_name --database-pass "$db_pwd" \ + --admin-user "admin" --admin-pass "$admin_password" \ + --data-dir "$datadir" \ + || ynh_die --message="Unable to install Nextcloud" + +#================================================= +# CONFIGURE NEXTCLOUD +#================================================= +ynh_script_progression --message="Configuring Nextcloud..." --weight=8 + +# Ensure that UpdateNotification app is disabled +exec_occ app:disable updatenotification + +# Enable LDAP plugin +exec_occ app:enable user_ldap +exec_occ ldap:create-empty-config + +# Load the installation config file in Nextcloud +nc_conf="$final_path/config_install.json" +ynh_add_config --template="../conf/config_install.json" --destination="$nc_conf" + +exec_occ config:import "$nc_conf" + +# Then remove the config file +ynh_secure_remove --file="$nc_conf" + +# Load the additional config file (used also for upgrade) +nc_conf="$final_path/config.json" +ynh_add_config --template="../conf/config.json" --destination="$nc_conf" + +exec_occ config:import "$nc_conf" + +# Then remove the config file +ynh_secure_remove --file="$nc_conf" + +#================================================= +# ADD A CRON JOB +#================================================= + +cron_path="/etc/cron.d/$app" +ynh_add_config --template="../conf/nextcloud.cron" --destination="$cron_path" +chown root: "$cron_path" +chmod 644 "$cron_path" + +exec_occ background:cron + + +#================================================= +# GENERIC FINALIZATION +#================================================= +# SECURE FILES AND DIRECTORIES +#================================================= + +# Fix app ownerships & permissions +chown -R $app: "$final_path" "$datadir" +find $final_path/ -type f -print0 | xargs -0 chmod 0644 +find $final_path/ -type d -print0 | xargs -0 chmod 0755 +find $datadir/ -type f -print0 | xargs -0 chmod 0640 +find $datadir/ -type d -print0 | xargs -0 chmod 0750 +chmod 640 "$final_path/config/config.php" +chmod 755 /home/yunohost.app + +#================================================= +# SETUP FAIL2BAN +#================================================= +ynh_script_progression --message="Configuring Fail2Ban..." --weight=8 + +# Create a dedicated Fail2Ban config +ynh_add_fail2ban_config --logpath="/home/yunohost.app/$app/data/nextcloud.log" --failregex="^.*Login failed: '.*' \(Remote IP: ''.*$" --max_retry=5 + +#================================================= +# SETUP SSOWAT +#================================================= +ynh_script_progression --message="Configuring SSOwat..." + +ynh_permission_create --permission="api" --label="api" --url="re:$domain\/.well-known\/.*" --allowed="visitors" "all_users" --auth_header="false" --show_tile="false" --protected="true" + +#================================================= +# RELOAD NGINX +#================================================= +ynh_script_progression --message="Reloading NGINX web server..." + +ynh_systemd_action --service_name=nginx --action=reload + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Installation of $app completed" --last