#!/bin/bash
# This file is part of web-conf which configures web servers
# Copyright (C) 2024 Ian Kelling
# 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 .
# SPDX-License-Identifier: GPL-3.0-or-later
[[ $EUID == 0 ]] || exec sudo -E "$BASH_SOURCE" "$@"
set -eE -o pipefail
trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
readonly this_file="$(readlink -f -- "${BASH_SOURCE[0]}")"
readonly this_dir="${this_file%/*}"
shopt -s nullglob # used in apache config file expansion
usage() {
cat </dev/null; then
# cerbot needs an existing virtualhost.
$0 -p 80 $t $h
# when generating an example config, add all relevant security options:
# --hsts --staple-ocsp --uir --must-staple
certbot certonly -n --email $email --no-self-upgrade \
--agree-tos --${t%2} -d $h
# cleanup the call to ourselves a short bit ago
rm $se/$h.conf
fi
# these scripts only run on renew, that is kinda dumb.
export RENEWED_LINEAGE=/etc/letsencrypt/live/$h
for script in /etc/letsencrypt/renewal-hooks/deploy/*; do
if [[ -x $script ]]; then
"$script"
fi
done
fi
if [[ $t == apache2 ]]; then
rm -f $se/000-default.conf
# note, we exepct ServerRoot of /etc/apache2
# apache requires exactly 1 listen directive per port (when no ip is also given),
# so we have to parse the config to do it programatically.
listen_80=false
listen_port=false
cd /etc/apache2
conf_files=(apache2.conf)
for (( i=0; i < ${#conf_files[@]}; i++ )); do
f="${conf_files[i]}"
# note: globs are expanded here.
conf_files+=( $(sed -rn "s,^\s*Include(Optional)?\s+(\S+).*,\2,p" "$f") )
case $(readlink -f "$f") in
$vhost_file|$redir_file) continue ;;
esac
for p in $(sed -rn "s,^\s*listen\s+(\S+).*,\1,Ip" "$f"); do
case $p in
80) listen_80=true ;;&
$port) listen_port=true ;;
esac
done
done
echo "$0: creating $vhost_file"
cat >$vhost_file <
ServerName $h
ServerAlias www.$h
DocumentRoot $root
EOF
if $do_root_settings; then
cat >>$vhost_file <
Options -Indexes ${symlinkarg}FollowSymlinks
EOF
fi
if [[ $extra_settings ]]; then
cat -- $extra_settings >>$vhost_file
fi
# go faster!
if [[ -e /etc/apache2/mods-available/http2.load ]]; then
# https://httpd.apache.org/docs/2.4/mod/mod_http2.html
a2enmod -q http2
cat >>$vhost_file <>$vhost_file <>$vhost_file </dev/null <<'EOF'
#https://webmasters.stackexchange.com/questions/124635/apache-redirect-http-to-https-without-preventing-http
Redirect permanent "/" "https://mydomain.ltd/"
# or, with generic rewrite, we use this on gnu.org
RewriteEngine on
RewriteCond %{HTTP:Upgrade-Insecure-Requests} "^1$"
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=307]
EOF
cat >$redir_file <
ServerName $h
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog \${APACHE_LOG_DIR}/error.log
CustomLog \${APACHE_LOG_DIR}/access.log vhost_time_combined
RewriteEngine on
RewriteCond %{SERVER_NAME} =$h
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]
EOF
if ! $listen_80; then
cat >>$redir_file <<'EOF'
Listen 80
EOF
fi
fi
# this is a copy of a file certbot, see below.
echo "$0: creating $common_ssl_conf"
cat >$common_ssl_conf <<'EOF'
# This file contains important security parameters. If you modify this file
# manually, Certbot will be unable to automatically provide future security
# updates. Instead, Certbot will print and log an error message with a path to
# the up-to-date file that you will need to refer to when manually updating
# this file. Contents are based on https://ssl-config.mozilla.org
SSLEngine on
# Intermediate configuration, tweak to your needs
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
SSLSessionTickets off
SSLOptions +StrictRequire
# Add vhost name to log entries:
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" vhost_combined
LogFormat "%v %h %l %u %t \"%r\" %>s %b" vhost_common
EOF
upstream=https://raw.githubusercontent.com/certbot/certbot/master/certbot-apache/certbot_apache/_internal/tls_configs/current-options-ssl-apache.conf
if ! diff -u <(wget -q -O - $upstream) $common_ssl_conf; then
cat <>$vhost_file <<'EOF'
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log vhost_time_combined
EOF
if ! $listen_port; then
# reference: https://httpd.apache.org/docs/2.4/mod/mpm_common.html#listen
cat >>$vhost_file </dev/null; then
# fun fact: nginx can be configured to do http2 without ssl.
ssl_arg+=" http2"
fi
fi
cat >$common_ssl_conf <<'EOF'
# let's encrypt gives us a bad nginx config, so use this:
# https://mozilla.github.io/server-side-tls/ssl-config-generator/
# using modern config. last checked 2017/4/22
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
ssl_dhparam /etc/nginx/dh2048.pem;
# modern configuration. tweak to your needs.
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;
# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;
# OCSP Stapling ---
# fetch OCSP records from URL in ssl_certificate and cache them
ssl_stapling on;
ssl_stapling_verify on;
## verify chain of trust of OCSP response using Root CA and Intermediate certs
# ian: commented out, unnecessary for le certs or my nginx ver.
#ssl_trusted_certificate $cert_dir/fullchain.pem;;
# ian: commented out, our local dns is expected to work fine.
#resolver ;
EOF
cat >$vhost_file <>$vhost_file <>$vhost_file <>$vhost_file <$redir_file <>$vhost_file
fi
if [[ $proxy ]]; then
cat >>$vhost_file <>$vhost_file </etc/apache2/conf-enabled/local-custom.conf <<'EOF'
# vhost_combined with %D (request time in microseconds)
# this file is just a convenient place to drop it.
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" %D" vhost_time_combined
SSLStaplingCache shmcb:/var/run/apache2/stapling_cache(128000)
EOF