2 # Copyright (C) 2016 Ian Kelling
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
16 [[ $EUID == 0 ]] ||
exec sudo
-E "$BASH_SOURCE" "$@"
19 trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
21 readonly this_file
="$(readlink -f -- "${BASH_SOURCE[0]}")"
22 readonly this_dir
="${this_file%/*}"
24 shopt -s nullglob
# used in apache config file expansion
28 Usage: ${0##*/} [OPTIONS] [EXTRA_SETTINGS_FILE] apache2|nginx DOMAIN
29 apache/nginx config & let's encrypt
31 If using tls then it expects certbot to be installed and in PATH. Also,
32 certbot cronjob should be taken care of outside this script. In the
33 debian package, it installs a systemd timer. If a script exists (I
34 expect it only on my , Ian Kelling's sytem) we install a systemd timer
35 to on failure. You can see the relevant script in my git repo
36 distro-setup, and log-quiet.
39 /a/bin/distro-setup/certbot-renew-hook
43 EXTRA_SETTINGS_FILE can be - for stdin
44 -a IPv4_ADDR IP address to listen on. Default all addresses.
45 ipv6 address support could be added to this script.
46 -e EMAIL Contact address for let's encrypt. Default is
47 root@\$(hostname --fqdn')
48 which is root@$(hostname --fqdn) on this host.
49 -f [ADDR:]PORT Enable proxy to [ADDR:]PORT. ADDR default is 127.0.0.1
51 -p PORT Main port to listen on, default 443. 80 implies -i.
53 -s Allow symlinks from the doucmentroot
54 -h|--help Print help and exit
56 Note: Uses GNU getopt options parsing style
61 ##### begin command line parsing ########
67 temp
=$
(getopt
-l help a
:e
:if:p
:r
:sh
"$@") || usage
1
76 -e) email
="$2"; shift 2 ;;
77 -f) proxy
="$2"; shift 2 ;;
78 -i) ssl
=false
; shift ;;
79 -p) port
="$2"; shift 2 ;;
80 -r) root
="$2"; shift 2 ;;
81 -s) symlinkarg
=+; shift ;;
84 *) echo "$0: Internal error!" ; exit 1 ;;
89 if (( ${#@} == 3 )); then
90 read -r extra_settings t h
<<<"${@}"
97 *) echo "$0: error: expected apache2 or nginx arg"; usage
1 ;;
101 echo "$0: error: expected domain and type arg"
105 if [[ ! $root ]]; then
106 root
=/var
/www
/$h/html
109 if [[ $proxy ]]; then
110 [[ $proxy == *:* ]] || proxy
=127.0.0.1:$proxy
113 if [[ ! $email ]]; then
114 email
=root@$
(hostname
--fqdn)
118 ##### end command line parsing ########
120 se
=/etc
/$t/sites-enabled
121 cert_dir
=/etc
/letsencrypt
/live
/$h
126 vhost_file
=$se/$h.conf
129 vhost_file
=$se/$h-$port.conf
132 redir_file
=$se/$h-redir.conf
134 if [[ $port == 80 ]]; then
136 # remove any thats hanging around
143 $this_dir/certbot-setup
$t
145 f
=$cert_dir/fullchain.pem
146 threedays
=259200 # in seconds
147 if [[ ! -e $f ]] ||
! openssl x509
-checkend $threedays -noout -in $f >/dev
/null
; then
148 # cerbot needs an existing virtualhost.
150 # when generating an example config, add all relevant security options:
151 # --hsts --staple-ocsp --uir --must-staple
152 certbot certonly
-n --email $email --no-self-upgrade \
153 --agree-tos --${t%2} -d $h
154 # cleanup the call to ourselves a short bit ago
157 # these scripts only run on renew, that is kinda dumb.
158 export RENEWED_LINEAGE
=/etc
/letsencrypt
/live
/$h
159 for script in /etc
/letsencrypt
/renewal-hooks
/deploy
/*; do
160 if [[ -x $script ]]; then
167 if [[ $t == apache2
]]; then
168 rm -f $se/000-default.conf
169 # note, we exepct ServerRoot of /etc/apache2
170 # apache requires exactly 1 listen directive per port (when no ip is also given),
171 # so we have to parse the config to do it programatically.
175 conf_files
=(apache2.conf
)
178 for (( i
=0; i
< ${#conf_files[@]}; i
++ )); do
180 # note: globs are expanded here.
181 conf_files
+=( $
(sed -rn "s,^\s*Include(Optional)?\s+(\S+).*,\2,p" "$f") )
182 case $
(readlink
-f "$f") in
183 $vhost_file|
$redir_file) continue ;;
185 for p
in $
(sed -rn "s,^\s*listen\s+(\S+).*,\1,Ip" "$f"); do
187 80) listen_80
=true
;;&
188 $port) listen_port
=true
;;
193 echo "$0: creating $vhost_file"
194 cat >$vhost_file <<EOF
195 <VirtualHost $vhostip:$port>
200 Options -Indexes ${symlinkarg}FollowSymlinks
204 if [[ $extra_settings ]]; then
205 cat -- $extra_settings >>$vhost_file
209 if [[ -e /etc
/apache
2/mods-available
/http2.load
]]; then
210 # https://httpd.apache.org/docs/2.4/mod/mod_http2.html
212 cat >>$vhost_file <<EOF
213 Protocols h2 http/1.1
217 if [[ $proxy ]]; then
218 a2enmod
-q proxy proxy_http
219 # fyi: trailing slash is important
220 # reference: https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html
221 # retry=0: https://stackoverflow.com/questions/683052/why-am-i-getting-an-apache-proxy-503-error
222 cat >>$vhost_file <<EOF
223 ProxyPass "/" "http://$proxy/" retry=0
224 ProxyPassReverse "/" "http://$proxy/"
232 common_ssl_conf
=/etc
/apache
2/common-ssl.conf
233 cat >>$vhost_file <<EOF
234 SSLCertificateFile $cert_dir/fullchain.pem
235 SSLCertificateKeyFile $cert_dir/privkey.pem
236 Include $common_ssl_conf
237 # From cerbot generated config example, taken 4/2017,
238 # should be rechecked once a year or so.
239 Header always set Strict-Transport-Security "max-age=31536000"
241 Header always set Content-Security-Policy upgrade-insecure-requests
244 if (( port
== 443 )); then
245 echo "$0: creating $redir_file"
246 cat >$redir_file <<EOF
249 ServerAdmin webmaster@localhost
250 DocumentRoot /var/www/html
252 ErrorLog \${APACHE_LOG_DIR}/error.log
253 CustomLog \${APACHE_LOG_DIR}/access.log vhost_time_combined
256 RewriteCond %{SERVER_NAME} =$h
257 RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]
260 if ! $listen_80; then
261 cat >>$redir_file <<'EOF'
267 # this is a copy of a file certbot, see below.
268 echo "$0: creating $common_ssl_conf"
269 cat >$common_ssl_conf <<'EOF'
270 # This file contains important security parameters. If you modify this file
271 # manually, Certbot will be unable to automatically provide future security
272 # updates. Instead, Certbot will print and log an error message with a path to
273 # the up-to-date file that you will need to refer to when manually updating
278 # Intermediate configuration, tweak to your needs
279 SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
280 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
281 SSLHonorCipherOrder off
282 SSLSessionTickets off
284 SSLOptions +StrictRequire
286 # Add vhost name to log entries:
287 LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" vhost_combined
288 LogFormat "%v %h %l %u %t \"%r\" %>s %b" vhost_common
291 upstream
=https
://raw.githubusercontent.com
/certbot
/certbot
/master
/certbot-apache
/certbot_apache
/_internal
/tls_configs
/current-options-ssl-apache.conf
292 if ! diff -u <(wget
-q -O - $upstream) $common_ssl_conf; then
299 upstream ssl settings differ from the snapshot we have taken!!!
300 We diffed with this command:
301 diff -c <(wget -q -O - $upstream) $common_ssl_conf
302 Update this script to take care this warning!!!!!
308 cat >>$vhost_file <<'EOF'
309 ErrorLog ${APACHE_LOG_DIR}/error.log
310 CustomLog ${APACHE_LOG_DIR}/access.log vhost_time_combined
314 if ! $listen_port; then
315 # reference: https://httpd.apache.org/docs/2.4/mod/mpm_common.html#listen
316 cat >>$vhost_file <<EOF
317 listen ${listenip}${port}${https_arg}
322 a2enmod
-q ssl rewrite
# rewrite needed for httpredir
323 service apache2 restart
325 # I rarely look at how much traffic I get, so let's keep that info
326 # around for longer than the default of 2 weeks.
327 sed -ri --follow-symlinks 's/^(\s*rotate\s).*/\1 365/' /etc
/logrotate.d
/apache2
328 fi ###### end if apache
330 if [[ $t == nginx
]]; then
331 common_ssl_conf
=/etc
/nginx
/common-ssl.conf
335 [[ -e dh2048.pem
]] || openssl dhparam
-out dh2048.pem
2048
339 if nginx
-V |
& grep -- '--with-http_v2_module\b' &>/dev
/null
; then
340 # fun fact: nginx can be configured to do http2 without ssl.
345 cat >$common_ssl_conf <<'EOF'
346 # let's encrypt gives us a bad nginx config, so use this:
347 # https://mozilla.github.io/server-side-tls/ssl-config-generator/
348 # using modern config. last checked 2017/4/22
349 ssl_session_timeout 1d;
350 ssl_session_cache shared:SSL:50m;
351 ssl_session_tickets off;
353 # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
354 ssl_dhparam /etc/nginx/dh2048.pem;
356 # modern configuration. tweak to your needs.
357 ssl_protocols TLSv1.2;
358 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';
359 ssl_prefer_server_ciphers on;
361 # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
362 add_header Strict-Transport-Security max-age=15768000;
365 # fetch OCSP records from URL in ssl_certificate and cache them
367 ssl_stapling_verify on;
369 ## verify chain of trust of OCSP response using Root CA and Intermediate certs
370 # ian: commented out, unnecessary for le certs or my nginx ver.
371 #ssl_trusted_certificate $cert_dir/fullchain.pem;;
373 # ian: commented out, our local dns is expected to work fine.
374 #resolver <IP DNS resolver>;
376 cat >$vhost_file <<EOF
378 server_name $h www.$h;
380 listen $listenip$port $ssl_arg;
382 if [[ ! $listenip ]]; then
383 cat >>$vhost_file <<EOF
384 listen [::]:$port $ssl_arg;
387 cat >>$vhost_file <<EOF
393 cat >>$vhost_file <<EOF
394 ssl_certificate $cert_dir/fullchain.pem;
395 ssl_certificate_key $cert_dir/privkey.pem;
396 include $common_ssl_conf;
399 if (( port
== 443 )); then
400 cat >$redir_file <<EOF
402 server_name $h www.$h;
403 listen 80 $http2_arg;
404 listen [::]:80 $http2_arg;
405 return 301 https://$server_name$request_uri;
411 if [[ $extra_settings ]]; then
412 cat $extra_settings >>$vhost_file
415 if [[ $proxy ]]; then
416 cat >>$vhost_file <<EOF
418 proxy_set_header Host \$host;
419 proxy_set_header X-Real-IP \$remote_addr;
420 proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
421 proxy_set_header X-Forwarded-Ssl on;
422 proxy_set_header X-Forwarded-Port $port;
423 proxy_pass http://$proxy;
428 cat >>$vhost_file <<EOF
433 service nginx restart
435 fi ####### end if nginx
437 cat >/etc
/apache
2/conf-enabled
/local-custom.conf
<<'EOF'
438 # vhost_combined with %D (request time in microseconds)
439 # this file is just a convenient place to drop it.
440 LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" %D" vhost_time_combined
441 SSLStaplingCache shmcb:/var/run/apache2/stapling_cache(128000)