2
\$\begingroup\$

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
Sᴀᴍ Onᴇᴌᴀ
29.5k16 gold badges45 silver badges201 bronze badges
asked Jun 24, 2022 at 17:17
\$\endgroup\$
5
  • \$\begingroup\$ Hi Roadrunner85. This site is for code reviews, which isn't what you sound like you are looking for. Asking about your web server settings probably belongs on another Stack Exchange site like security, webmasters, or Unix&Linux. \$\endgroup\$ Commented Jun 24, 2022 at 20:05
  • \$\begingroup\$ @chicks Sorry, my bad. Please feel free to remove my question. It seems that I cannot do it by myself. \$\endgroup\$ Commented Jun 24, 2022 at 20:56
  • \$\begingroup\$ I’m voting to close this question because it doesn't look for a review. There's Which computer science / programming Stack Exchange sites do I post on? \$\endgroup\$ Commented Jun 26, 2022 at 14:35
  • \$\begingroup\$ @greybeard please close it. Sorry for any inconveniences. \$\endgroup\$ Commented Jun 27, 2022 at 15:05
  • 1
    \$\begingroup\$ I don't see a problem here? if there are recommendations to improve the script is a clear request for a review \$\endgroup\$ Commented Jun 28, 2022 at 19:46

1 Answer 1

2
\$\begingroup\$
set -e # Terminate if script has error

The comment doesn't add value - assume that anybody reading either knows what the flag means or can read the Bash manual.

Consider adding -u and -o pipefail as other useful settings.


printf "
####################################################################
# Enter location on disk for site (Default: /var/www/\$fqn):
####################################################################\n
dir: "
read -rp "" sitedir

That's a bit odd. I recommend using %s rather than interpolating a variable directly into the printf format string. Also, printing a partial line will confuse read (which assumes that it begins at start of line). Even more strangely, you specifically pass -p "". I'd replace that with read -rp 'dir: ' sitedir.


if [ -n "$sitedir" ]; then
 if [ ${sitedir:+1} ]
 then
 echo "yes"
 fi
else
 sitedir="/var/www/$fqn"
fi

What's going on here? Just use the usual parameter defaulting:

echo "sitedir=${sitedir:=/var/www/$fqn}"

# 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

Could be simplified if we create a variable:

hostname=${sub:+$sub.}$fqn
servername="server_name $hostname www.$hostname;"
vhostserver="ServerName $hostname
ServerAlias www.$hostname
answered Jun 28, 2022 at 7:53
\$\endgroup\$
2
  • \$\begingroup\$ By the way, is it possible that there is a typo in the definition of $hostname? I assume it has to be defined as hostname=${sub:+$sub.}$fqn otherwise you get a wrong hostname in case no subdomain is provided by the user. This feature was not meant to be obligatory. \$\endgroup\$ Commented Jun 29, 2022 at 20:20
  • 1
    \$\begingroup\$ Yes, a typo - now fixed. Thank you! \$\endgroup\$ Commented Jun 30, 2022 at 6:05

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.