The following bash script is intended to be run on a freshly installed digitaloceans ubuntu 20.04 droplet by root in its home dir.
The purpose is to setup a ready to use webserver with SSL (certbot), PHP 7.4 support (fastcgi), an Apache2 fileserver and nginx as reverse proxy. DNS is managed by Cloudflare.
My main questions are if the settings for apache2, nginx and fastcgi are justifiable and if there are recommendations to improve the script.
#!/bin/bash
set -e # Terminate if script has error
####################################################################
# Variables used
####################################################################
sitedir=""
fqn=""
sdub=""
myip=""
cftoken=""
cfmail=""
webmail=""
servername=""
vhostserver=""
myip="$(curl ipecho.net/plain; echo)"
####################################################################
# Query for location
####################################################################
printf "
####################################################################
# Enter location on disk for site (Default: /var/www/\$fqn):
####################################################################\n
dir: "
read -rp "" sitedir
####################################################################
# Query for FQN
####################################################################
printf "
####################################################################
# Enter FQN for website:
####################################################################\n
fqn: "
read -rp "" fqn
####################################################################
# Query for subdomain
####################################################################
printf "
####################################################################
# Enter subdomain for FQN:
####################################################################\n
sub: "
read -rp "" sub
####################################################################
# Query for webmaster mail address
####################################################################
printf "
####################################################################
# Enter webmaster mail address for site: (Default: webmaster@\$fqn)
####################################################################\n
webmaster: "
read -rp "" webmail
####################################################################
# Query non-root user credentials
####################################################################
printf "
####################################################################
# Enter CLOUDFLARE-TOKEN:
####################################################################\n
cftoken: "
read -rp "" cftoken
####################################################################
# Query non-root user credentials
####################################################################
printf "
####################################################################
# Enter CLOUDFLARE-mail:
####################################################################\n
cfmail: "
read -rp "" cfmail
####################################################################
# setting defaults
####################################################################
# sitedir
if [ -n "$sitedir" ]; then
if [ ${sitedir:+1} ]
then
echo "yes"
fi
else
sitedir="/var/www/$fqn"
fi
# webmaster mail
if [ -n "$webmail" ]; then
if [ ${webmail:+1} ]
then
echo "yes"
fi
else
webmail=webmaster@$fqn
fi
# servername
if [ -n "$sub" ]; then
servername="server_name $sub.$fqn www.$sub.$fqn;"
vhostserver="ServerName $sub.$fqn
ServerAlias www.$sub.$fqn"
else
servername="server_name $fqn www.$fqn;"
vhostserver="ServerName $fqn
ServerAlias www.$fqn"
fi
####################################################################
# Enable basic firewall rules
####################################################################
ufw allow OpenSSH
ufw enable
####################################################################
# Install required software
####################################################################
apt update
apt -y upgrade
# EXA
wget -c http://old-releases.ubuntu.com/ubuntu/pool/universe/r/rust-exa/exa_0.9.0-4_amd64.deb
apt-get install ./exa_0.9.0-4_amd64.deb
# basics
apt -y install tree gnupg2 git net-tools original-awk fzf silversearcher-ag unzip pkg-config \
openssl apache2 php-fpm php-xml libtool
# fastcgi for apache2
wget https://mirrors.edge.kernel.org/ubuntu/pool/multiverse/liba/libapache-mod-fastcgi/libapache2-mod-fastcgi_2.4.7~0910052141-1.2_amd64.deb
dpkg -i libapache2-mod-fastcgi_2.4.7~0910052141-1.2_amd64.deb
####################################################################
# setup of apache2
####################################################################
# set apache listening on loopback for ipv4 only
mv /etc/apache2/ports.conf /etc/apache2/ports.conf.default
echo "Listen 0.0.0.0:8080" | tee /etc/apache2/ports.conf
# make working dir for server
mkdir -p $sitedir
# disable factory defaults
a2dissite 000-default
# configure apache for use of mod_fastcgi
a2enmod actions
a2enmod proxy_fcgi setenvif
systemctl restart apache2
a2enconf php7.4-fpm
systemctl reload apache2
# backup default fastcgi.conf
mv /etc/apache2/mods-enabled/fastcgi.conf /etc/apache2/mods-enabled/fastcgi.conf.default
# setting new defaults for fastcgi
cat << EOF > /etc/apache2/mods-enabled/fastcgi.conf
<IfModule mod_fastcgi.c>
AddHandler fastcgi-script .fcgi
FastCgiIpcDir /var/lib/apache2/fastcgi
AddType application/x-httpd-fastphp .php
Action application/x-httpd-fastphp /php-fcgi
Alias /php-fcgi /usr/lib/cgi-bin/php-fcgi
FastCgiExternalServer /usr/lib/cgi-bin/php-fcgi -socket /run/php/php7.4-fpm.sock -pass-header Authorization
<Directory /usr/lib/cgi-bin>
Require all granted
</Directory>
</IfModule>
EOF
systemctl restart apache2
# create a php-info page - to be removed before production
echo "<?php phpinfo(); ?>" | tee /var/www/$fqn/info.php
# adjust ufw
ufw allow 8080
ufw allow "Apache Full"
# mod_rpaf to get visitors real IP through cloudflare and reverse proxy
apt -y install build-essential apache2-dev libtool-bin
wget https://github.com/gnif/mod_rpaf/archive/stable.zip
unzip stable.zip && cd mod_rpaf-stable
make && make install && libtool --finish /usr/lib/apache2/modules
touch /etc/apache2/mods-available/rpaf.load
echo 'LoadModule rpaf_module /usr/lib/apache2/modules/mod_rpaf.so' >> /etc/apache2/mods-available/rpaf.load
a2enmod rpaf
systemctl restart apache2
# Set defaults for virtual host
cat << EOF > /etc/apache2/sites-available/$fqn.conf
<VirtualHost *:8080>
$vhostserver
ServerAdmin $webmail
DocumentRoot $sitedir
<IfModule mod_ssl.c>
SSLEngine On
SSLHonorCipherOrder On
SSLCompression Off
SSLSessionTickets Off
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite EECDH+CHACHA20:EECDH+AES
</IfModule>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} =on
RewriteRule ^ - [E=PROTO:https]
RewriteCond %{HTTPS} !=on
RewriteRule ^ - [E=PROTO:http]
# RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ %{ENV:PROTO}://%1%{REQUEST_URI} [R=301,L]
</IfModule>
<IfModule mod_headers.c>
Header always set Referrer-Policy "strict-origin-when-cross-origin" "expr=%{CONTENT_TYPE} =~ m#text\/(css|html|javascript)|application\/pdf|xml#i"
</IfModule>
<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
</IfModule>
<IfModule mod_headers.c>
Header always set X-Frame-Options "DENY" "expr=%{CONTENT_TYPE} =~ m#text/html#i"
</IfModule>
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
<FilesMatch "\.(avifs?|bmp|cur|gif|ico|jpe?g|jxl|a?png|svgz?|webp)$">
SetEnvIf Origin ":" IS_CORS
Header set Access-Control-Allow-Origin "*" env=IS_CORS
</FilesMatch>
</IfModule>
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(eot|otf|tt[cf]|woff2?)$">
Header set Access-Control-Allow-Origin "*"
</FilesMatch>
</IfModule>
<IfModule rpaf_module>
RPAF_Enable On
RPAF_SetHostName On
RPAF_ProxyIPs $myip
RPAF_Header CF-CONNECTING-IP
</IfModule>
# Log format config
LogFormat "%{CF-CONNECTING-IP}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" forwarded
CustomLog "/var/log/apache2/$fqn.access.log" forwarded
ErrorLog /var/log/apache2/$fqn.error.log
<Directory $sitedir>
AllowOverride All
</Directory>
</VirtualHost>
EOF
a2ensite $fqn.conf
systemctl restart apache2
# get SSL certificates by certbot
apt-get -y install python3-certbot-dns-cloudflare
# setup CF credentials
mkdir /root/.secrets/ && touch /root/.secrets/cloudflare.ini
echo "dns_cloudflare_email=$cfmail" >> /root/.secrets/cloudflare.ini
echo "dns_cloudflare_api_key=$cftoken" >> /root/.secrets/cloudflare.ini
chmod 0700 /root/.secrets/
chmod 0400 /root/.secrets/cloudflare.ini
# go
certbot certonly --agree-tos --no-eff-email --email \
$webmail --dns-cloudflare --dns-cloudflare-credentials \
/root/.secrets/cloudflare.ini -d $fqn,*.$fqn --preferred-challenges dns-01
####################################################################
# nginx setup
####################################################################
apt -y install nginx
# remove default
rm /etc/nginx/sites-enabled/default
# setup new defaults
cat << EOF > /etc/nginx/sites-available/apache
server {
listen 80;
$servername
location / {
return 301 https://\$host\$request_uri;
}
}
server {
listen [::]:443 ssl http2;
listen 443 ssl http2;
$servername
ssl_session_timeout 24h;
ssl_session_cache shared:SSL:10m;
keepalive_timeout 300s;
# ssl_buffer_size 1400;
ssl_session_tickets off;
ssl_prefer_server_ciphers on;
ssl_certificate /etc/letsencrypt/live/$fqn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/$fqn/privkey.pem;
ssl_dhparam /usr/lib/python3/dist-packages/certbot/ssl-dhparams.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers EECDH+CHACHA20:EECDH+AES;
ssl_ecdh_curve X25519:prime256v1:secp521r1:secp384r1;
return 301 $scheme://www.$fqn$request_uri;
}
server {
listen [::]:443 ssl http2;
listen 443 ssl http2;
server_name www.$fqn
ssl_session_timeout 24h;
ssl_session_cache shared:SSL:10m;
keepalive_timeout 300s;
# ssl_buffer_size 1400;
ssl_session_tickets off;
ssl_prefer_server_ciphers on;
ssl_certificate /etc/letsencrypt/live/$fqn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/$fqn/privkey.pem;
ssl_dhparam /usr/lib/python3/dist-packages/certbot/ssl-dhparams.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers EECDH+CHACHA20:EECDH+AES;
ssl_ecdh_curve X25519:prime256v1:secp521r1:secp384r1;
location / {
proxy_pass http://$myip:8080;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_read_timeout 600;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
client_max_body_size 100M;
}
location ~ /\.ht {
deny all;
}
# add_header Referrer-Policy $referrer_policy always;
add_header X-Content-Type-Options nosniff always;
# add_header X-Frame-Options $x_frame_options always;
location ~* /\.(?!well-known\/) {
deny all;
}
location ~* (?:#.*#|\.(?:bak|conf|dist|fla|in[ci]|log|orig|psd|sh|sql|sw[op])|~)$ {
deny all;
}
# add_header Access-Control-Allow-Origin $cors;
access_log /var/log/nginx/$fqn.access.log;
error_log /var/log/nginx/$fqn.error.log;
}
EOF
# activate new settings
ln -s /etc/nginx/sites-available/apache /etc/nginx/sites-enabled/apache
systemctl restart nginx