#!/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