#!/bin/bash ############################################################# # Author: Taryel Hlontsi, 2022 # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see ############################################################# ############################################################# # SETUP SECTION, use "sudo su -- root" ############################################################# # if set to true, ssh login only with ssh key which must(!) be configured upfront NO_PASSWORD_SSH=true # only if new user is required (root login won't be allowed anyway) OS_USER="ubuntu" OS_USER_PASSWORD="password_goes_here" # change the IP. If domain is used then set it here as the server name OS_SERVER_NAME="nextcloud" OS_IP="127.0.0.1" # database setup for the nexcloud web app NC_DB="nextcloud" NC_DB_USER="nextcloud" NC_DB_PASSWORD="qrsE1001^00000" # account to log on to the nexcloud web app NC_ADMIN_USER="tar" NC_ADMIN_PASSWORD="password_goes_here" # link for nextcloud, country is for ssl certificate and fixing some warnings NC_DOWNLOAD_URL="https://download.nextcloud.com/server/releases/latest.zip" NC_COUNTRY_CODE="PL" # optional, but removes some warnings in nextcloud PHP_TIMEZONE="Europe/Warsaw" # may be left as is LOG_DIR="/home" LOG="${LOG_DIR}/log.txt" ############################################################# # HELPER FUNCTIONS ############################################################# trace() { PURPLE='\033[1;35m' NC='\033[0m' echo -e "${PURPLE}$1${NC}" echo -e "TRACE:\t$1" >> $LOG } info() { GREEN='\033[1;32m' NC='\033[0m' echo -e "${GREEN}$1${NC}" echo -e "INFO:\t$1" >> $LOG } warn() { YELLOW='\033[1;33m' NC='\033[0m' echo -e "${YELLOW}$1${NC}" echo -e "WARN:\t$1" >> $LOG } error() { RED='\033[1;31m' NC='\033[0m' echo -e "${RED}$1${NC}" echo -e "ERROR:\t$1" >> $LOG } not_set() { count=$(grep -Pcx "$1" "$2") code=$? if (( $code > 0 )) || (( $count == 0 )); then true else false fi } not_exist() { if test ! -f "$2"; then warn "file does not exist: $2" true else count=$(grep -Pc "$1" "$2") code=$? if (( $code > 0 )) || (( $count == 0 )); then true else false fi fi } configure() { if not_exist "$1" "$3"; then warn "setting \"$1\" is not in the file, thus won't be changed" false else if not_set "$2" "$3"; then sed -i "s~.*${1}.*~${2}~gi" "$3" && trace "\"$1\" set to \"$2\"" && true else warn "\"$2\" already set in \"$3\"" false fi fi } configured() { count=$(grep -Pcx "$1" "$2") if (( $? > 0 )) || (( $count == 0 )); then false else true fi } insert_before() { if not_set "$2" "$3"; then sed -i "\~.*${1}.*~i ${2}" "$3" && trace "\"$1\" set to \"$2\"" && true else warn "\"$2\" already set in \"$3\"" false fi } ############################################################# # 1 - ADD USER ############################################################# add_user() { info "STEP 1 Adding a user" adduser --disabled-password --gecos "" $OS_USER chpasswd <<<"${OS_USER}:${OS_USER_PASSWORD}" usermod -aG sudo $OS_USER info "STEP 1 done" } ############################################################# # 2 - CHANGE SERVER NAME ############################################################# change_servername() { info "STEP 2 Changing server name" echo $OS_SERVER_NAME > /etc/hostname hostentry="127.0.1.1\t${OS_SERVER_NAME}" if not_set "$hostentry" "/etc/hostname"; then echo -e "$hostentry" >> /etc/hosts else trace "\"$hostentry\" already set in \"/etc/hosts\"" fi info "STEP 2 done" } ############################################################# # 3 - INSTALL MARIADB ############################################################# install_mariadb() { info "STEP 3 Installing Maria DB" apt install -y mariadb-server systemctl --quiet is-active mariadb || systemctl start mariadb systemctl --quiet is-enabled mariadb || systemctl enable mariadb mysql -u root < ${CFG_NAME} DocumentRoot "/var/www/${OS_SERVER_NAME}" ServerName ${OS_SERVER_NAME} Options MultiViews FollowSymlinks AllowOverride All Order allow,deny Allow from all TransferLog /var/log/apache2/${OS_SERVER_NAME}_access.log ErrorLog /var/log/apache2/${OS_SERVER_NAME}_error.log EOF trace "Apache config created: ${CFG_NAME}. Enabling it" a2ensite "${OS_SERVER_NAME}.conf" a2enmod dir env headers mime rewrite ssl systemctl restart apache2 info "STEP 6 done" } ############################################################# # 7 - SET UP NEXTCLOUD ############################################################# setup_nextcloud() { info "STEP 7 Setting up NEXTCLOUD" USER='www-data' NC_CONFIG="/var/www/${OS_SERVER_NAME}/config/config.php" trace "setting up ${NC_CONFIG}" cd "/var/www/${OS_SERVER_NAME}/" sudo -u www-data php occ maintenance:install \ --database "mysql" \ --database-name "${NC_DB}" \ --database-user "${NC_DB_USER}" \ --database-pass "${NC_DB_PASSWORD}" \ --database-host "localhost" \ --data-dir "/var/www/${OS_SERVER_NAME}/data" \ --admin-user "${NC_ADMIN_USER}" \ --admin-pass "${NC_ADMIN_PASSWORD}" trace "Nextcloud config creation result (0 means OK): $?" configure ';' " 'memcache.local' => '\\\OC\\\Memcache\\\APCu'," $NC_CONFIG && \ echo " 'default_phone_region' => '${NC_COUNTRY_CODE}'," >> $NC_CONFIG && \ echo ');' >> $NC_CONFIG if configured "\t2 => '${OS_IP}'," $NC_CONFIG; then warn "Trusted domains were already configured" else configure "0 => 'localhost'," "\t0 => 'localhost',\n\t1 => '${OS_SERVER_NAME}',\n\t2 => '${OS_IP}'," $NC_CONFIG trace "Trusted domains were set in ${NC_CONFIG}" fi sudo chmod 660 $NC_CONFIG trace "Changed mod of ${NC_CONFIG}" trace "Setting up a cron job for cron.php" cat < /etc/systemd/system/nextcloudcron.service [Unit] Description=Nextcloud cron.php job [Service] User=${USER} ExecStart=/usr/bin/php -f /var/www/${OS_SERVER_NAME}/cron.php --define apc.enable_cli=1 KillMode=process EOF cat < /etc/systemd/system/nextcloudcron.timer [Unit] Description=Run Nextcloud cron.php every 5 minutes [Timer] OnBootSec=5min OnUnitActiveSec=5min Unit=nextcloudcron.service [Install] WantedBy=timers.target EOF systemctl enable --now nextcloudcron.timer # alternative approach is to setup a crontab: #CRONTAB="/var/spool/cron/crontabs/${USER}" #echo "*/5 * * * * php -f /var/www/${OS_SERVER_NAME}/cron.php" >> $CRONTAB #sudo chmod 600 $CRONTAB #sudo chown $USER:$USER $CRONTAB # to delete the crontab: # crontab -r -u www-data info "STEP 7 done" } ############################################################# # 8 - INSTALL SELF-SIGNED SSL CERTIFICATE ############################################################# use_selfsigned_ssl_certificate() { info "STEP 8.1 Configuring a self-signed SSL certificate" KEY="/etc/ssl/private/apache.key" CRT="/etc/ssl/certs/apache.crt" APACHE_SSL_CFG="/etc/apache2/sites-available/default-ssl.conf" openssl req -x509 \ -nodes \ -days 2048 \ -newkey rsa:2048 \ -keyout $KEY \ -out $CRT \ -subj "/C=${NC_COUNTRY_CODE}/ST=${NC_COUNTRY_CODE}/L=${NC_COUNTRY_CODE}/O=E Corp/OU=IT/CN=${OS_SERVER_NAME}/emailAddress=info@example.com" trace "SSH key created at: ${KEY}" trace "Certificate created at: ${CRT}" CFG_NAME="/etc/apache2/sites-available/${OS_SERVER_NAME}-ssl.conf" cat < ${CFG_NAME} DocumentRoot "/var/www/${OS_SERVER_NAME}" ServerName $OS_SERVER_NAME SSLEngine on Header always set Strict-Transport-Security "max-age=15552000; includeSubDomains" Options MultiViews FollowSymlinks AllowOverride All Order allow,deny Allow from all SSLOptions +StdEnvVars SSLOptions +StdEnvVars TransferLog /var/log/apache2/${OS_SERVER_NAME}_access.log ErrorLog /var/log/apache2/${OS_SERVER_NAME}_error.log SSLCertificateFile ${CRT} SSLCertificateKeyFile ${KEY} SSLProtocol -all +TLSv1.2 +TLSv1.3 SSLHonorCipherOrder on SSLCipherSuite EECDH+AESGCM:EDH+AESGCM SSLCompression off EOF trace "New config for site created: ${CFG_NAME}. Enabling it" a2ensite "${OS_SERVER_NAME}-ssl.conf" trace "Setting up auto-redirect to HTTPS..." CFG_NAME="/etc/apache2/sites-available/${OS_SERVER_NAME}.conf" if configured ' RewriteEngine on' $CFG_NAME; then warn 'HTTPS redirection was already set up' else configure "ServerName ${OS_SERVER_NAME}" \ " ServerName ${OS_SERVER_NAME}\n RewriteEngine on\n RewriteCond %{HTTPS} !=on\n RewriteRule ^(.*)$ https://%{HTTP_HOST} [R=301,L]" \ $CFG_NAME trace "Entries added to ${CFG_NAME} to redirect all requests to HTTPS" fi systemctl restart apache2 info "STEP 8.1 done" } ############################################################# # 9 - SET AUTOUPDATES FOR THE SERVER ############################################################# set_autoupdates() { info "STEP 9 Setting up autoupdates (unattended-updates)" apt install -y unattended-upgrades export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true dpkg-reconfigure --priority=low unattended-upgrades UNT_CFG="/etc/apt/apt.conf.d/50unattended-upgrades" UNT_CFG_BACK="${LOG_DIR}/50unattended-upgrades.backup" trace "Going to rewrite ${UNT_CFG}" trace "Backup will be here: ${UNT_CFG_BACK}" if test -f "$UNT_CFG_BACK"; then warn "unattended updates config backup is already there!" else cp $UNT_CFG $UNT_CFG_BACK cat <<'EOF' > ${UNT_CFG} Unattended-Upgrade::Allowed-Origins { "${distro_id}:${distro_codename}"; "${distro_id}:${distro_codename}-security"; "${distro_id}ESMApps:${distro_codename}-apps-security"; "${distro_id}ESM:${distro_codename}-infra-security"; }; Unattended-Upgrade::Package-Blacklist { }; Unattended-Upgrade::DevRelease "auto"; Unattended-Upgrade::Remove-Unused-Kernel-Packages "true"; Unattended-Upgrade::Remove-New-Unused-Dependencies "true"; Unattended-Upgrade::Remove-Unused-Dependencies "true"; Unattended-Upgrade::Automatic-Reboot "true"; Unattended-Upgrade::Automatic-Reboot-Time "05:00"; EOF fi info "STEP 9 done" } ############################################################# # 10 - ADDITIONAL SECURITY FOR THE SERVER ############################################################# secure_server() { info "STEP 10 Securing the server" trace "Installing openssh server..." apt install -y openssh-server systemctl --quiet is-active sshd || systemctl start sshd systemctl --quiet is-enabled sshd || systemctl enable sshd SSH_CFG="/etc/ssh/sshd_config" SSH_CFG_BACK="${LOG_DIR}/sshd_config.backup" trace "Going to rewrite ${SSH_CFG}" trace "Backup will be here: ${SSH_CFG_BACK}" if test -f "$SSH_CFG_BACK"; then warn "SSH config backup is already there!" else cp $SSH_CFG $SSH_CFG_BACK cat < ${SSH_CFG} Include /etc/ssh/sshd_config.d/*.conf PermitRootLogin no ChallengeResponseAuthentication no UsePAM yes X11Forwarding yes PrintMotd no AcceptEnv LANG LC_* Subsystem sftp /usr/lib/openssh/sftp-server EOF if $NO_PASSWORD_SSH; then info "Disabling SSH with password completely!" echo 'PasswordAuthentication no' >> $SSH_CFG fi fi trace "Installing fail2ban..." apt install -y fail2ban systemctl --quiet is-active fail2ban || systemctl start fail2ban systemctl --quiet is-enabled fail2ban || systemctl enable fail2ban fail2ban-client status F2B_CFG_ORIG="/etc/fail2ban/jail.conf" F2B_CFG_COPY="/etc/fail2ban/jail.local" if test -f "$F2B_CFG_COPY"; then warn "Fail2ban was already set up" else cp $F2B_CFG_ORIG $F2B_CFG_COPY trace "Setting up ${F2B_CFG_COPY}" configure 'bantime = 10m' 'bantime = 60m' $F2B_CFG_COPY configure 'maxretry = 5' 'maxretry = 20' $F2B_CFG_COPY fi trace "Configuring ufw..." ufw --force enable ufw allow 'Apache Full' ufw allow 'OpenSSH' ufw status info "STEP 10 done" } ############################################################# # RUN SECTION ############################################################# user=$(whoami) if [ $user != root ]; then error "You are using a non-privileged account" exit 1 fi echo $(date +'%Y-%m-%d %H:%M') > $LOG info "Hiya!" apt update apt upgrade -y #add_user change_servername install_mariadb install_php_and_apache setup_php download_nextcloud setup_nextcloud use_selfsigned_ssl_certificate set_autoupdates secure_server info "Don't forget to reboot" info "Bye!" exit 0 # if domain name is available, SSL cert can be verified by Lets encrypt: # sudo snap install core; sudo snap refresh core # sudo snap install --classic certbot # sudo ln -s /snap/bin/certbot /usr/bin/certbot # sudo certbot certonly --apache # sudo certbot renew --dry-run # disable current ssl site in apache # create new ssl config based on old one, replace certificates and ServerName (here: /etc/apache2/sites-available/) # optional: replace ServerName in the http config as well # and a new domain name to the trusted_domains section in NC's config.php (here: /var/www/nextcloud/config/) # sudo systemctl restart apache2 # do not change ownership of /var/www/nextcloud, it should be owned by www-data # sometimes login page cannot be started after a fresh install # then try to run again: # sudo chown -R www-data:www-data /var/www/nextcloud # btw log is here: sudo vim /var/www/nextcloud/data/nextcloud.log