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
160 if [[ $t == apache2
]]; then
161 rm -f $se/000-default.conf
162 # note, we exepct ServerRoot of /etc/apache2
163 # apache requires exactly 1 listen directive per port (when no ip is also given),
164 # so we have to parse the config to do it programatically.
168 conf_files
=(apache2.conf
)
171 for (( i
=0; i
< ${#conf_files[@]}; i
++ )); do
173 # note: globs are expanded here.
174 conf_files
+=( $
(sed -rn "s,^\s*Include(Optional)?\s+(\S+).*,\2,p" "$f") )
175 case $
(readlink
-f "$f") in
176 $vhost_file|
$redir_file) continue ;;
178 for p
in $
(sed -rn "s,^\s*listen\s+(\S+).*,\1,Ip" "$f"); do
180 80) listen_80
=true
;;&
181 $port) listen_port
=true
;;
186 echo "$0: creating $vhost_file"
187 cat >$vhost_file <<EOF
188 <VirtualHost $vhostip:$port>
193 Options -Indexes ${symlinkarg}FollowSymlinks
197 if [[ $extra_settings ]]; then
198 cat -- $extra_settings >>$vhost_file
202 if [[ -e /etc
/apache
2/mods-available
/http2.load
]]; then
203 # https://httpd.apache.org/docs/2.4/mod/mod_http2.html
205 cat >>$vhost_file <<EOF
206 Protocols h2 http/1.1
210 if [[ $proxy ]]; then
211 a2enmod
-q proxy proxy_http
212 # fyi: trailing slash is important
213 # reference: https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html
214 # retry=0: https://stackoverflow.com/questions/683052/why-am-i-getting-an-apache-proxy-503-error
215 cat >>$vhost_file <<EOF
216 ProxyPass "/" "http://$proxy/" retry=0
217 ProxyPassReverse "/" "http://$proxy/"
225 common_ssl_conf
=/etc
/apache
2/common-ssl.conf
226 cat >>$vhost_file <<EOF
227 SSLCertificateFile $cert_dir/fullchain.pem
228 SSLCertificateKeyFile $cert_dir/privkey.pem
229 Include $common_ssl_conf
230 # From cerbot generated config example, taken 4/2017,
231 # should be rechecked once a year or so.
232 Header always set Strict-Transport-Security "max-age=31536000"
234 Header always set Content-Security-Policy upgrade-insecure-requests
237 if (( port
== 443 )); then
238 echo "$0: creating $redir_file"
239 cat >$redir_file <<EOF
242 ServerAdmin webmaster@localhost
243 DocumentRoot /var/www/html
245 ErrorLog \${APACHE_LOG_DIR}/error.log
246 CustomLog \${APACHE_LOG_DIR}/access.log vhost_time_combined
249 RewriteCond %{SERVER_NAME} =$h
250 RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]
253 if ! $listen_80; then
254 cat >>$redir_file <<'EOF'
260 # this is a copy of a file certbot, see below.
261 echo "$0: creating $common_ssl_conf"
262 cat >$common_ssl_conf <<'EOF'
263 # This file contains important security parameters. If you modify this file
264 # manually, Certbot will be unable to automatically provide future security
265 # updates. Instead, Certbot will print and log an error message with a path to
266 # the up-to-date file that you will need to refer to when manually updating
271 # Intermediate configuration, tweak to your needs
272 SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
273 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
274 SSLHonorCipherOrder off
275 SSLSessionTickets off
277 SSLOptions +StrictRequire
279 # Add vhost name to log entries:
280 LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" vhost_combined
281 LogFormat "%v %h %l %u %t \"%r\" %>s %b" vhost_common
284 upstream
=https
://raw.githubusercontent.com
/certbot
/certbot
/master
/certbot-apache
/certbot_apache
/_internal
/tls_configs
/current-options-ssl-apache.conf
285 if ! diff -u <(wget
-q -O - $upstream) $common_ssl_conf; then
292 upstream ssl settings differ from the snapshot we have taken!!!
293 We diffed with this command:
294 diff -c <(wget -q -O - $upstream) $common_ssl_conf
295 Update this script to take care this warning!!!!!
301 cat >>$vhost_file <<'EOF'
302 ErrorLog ${APACHE_LOG_DIR}/error.log
303 CustomLog ${APACHE_LOG_DIR}/access.log vhost_time_combined
307 if ! $listen_port; then
308 # reference: https://httpd.apache.org/docs/2.4/mod/mpm_common.html#listen
309 cat >>$vhost_file <<EOF
310 listen ${listenip}${port}${https_arg}
315 a2enmod
-q ssl rewrite
# rewrite needed for httpredir
316 service apache2 restart
318 # I rarely look at how much traffic I get, so let's keep that info
319 # around for longer than the default of 2 weeks.
320 sed -ri --follow-symlinks 's/^(\s*rotate\s).*/\1 365/' /etc
/logrotate.d
/apache2
321 fi ###### end if apache
323 if [[ $t == nginx
]]; then
324 common_ssl_conf
=/etc
/nginx
/common-ssl.conf
328 [[ -e dh2048.pem
]] || openssl dhparam
-out dh2048.pem
2048
332 if nginx
-V |
& grep -- '--with-http_v2_module\b' &>/dev
/null
; then
333 # fun fact: nginx can be configured to do http2 without ssl.
338 cat >$common_ssl_conf <<'EOF'
339 # let's encrypt gives us a bad nginx config, so use this:
340 # https://mozilla.github.io/server-side-tls/ssl-config-generator/
341 # using modern config. last checked 2017/4/22
342 ssl_session_timeout 1d;
343 ssl_session_cache shared:SSL:50m;
344 ssl_session_tickets off;
346 # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
347 ssl_dhparam /etc/nginx/dh2048.pem;
349 # modern configuration. tweak to your needs.
350 ssl_protocols TLSv1.2;
351 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';
352 ssl_prefer_server_ciphers on;
354 # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
355 add_header Strict-Transport-Security max-age=15768000;
358 # fetch OCSP records from URL in ssl_certificate and cache them
360 ssl_stapling_verify on;
362 ## verify chain of trust of OCSP response using Root CA and Intermediate certs
363 # ian: commented out, unnecessary for le certs or my nginx ver.
364 #ssl_trusted_certificate $cert_dir/fullchain.pem;;
366 # ian: commented out, our local dns is expected to work fine.
367 #resolver <IP DNS resolver>;
369 cat >$vhost_file <<EOF
371 server_name $h www.$h;
373 listen $listenip$port $ssl_arg;
375 if [[ ! $listenip ]]; then
376 cat >>$vhost_file <<EOF
377 listen [::]:$port $ssl_arg;
380 cat >>$vhost_file <<EOF
386 cat >>$vhost_file <<EOF
387 ssl_certificate $cert_dir/fullchain.pem;
388 ssl_certificate_key $cert_dir/privkey.pem;
389 include $common_ssl_conf;
392 if (( port
== 443 )); then
393 cat >$redir_file <<EOF
395 server_name $h www.$h;
396 listen 80 $http2_arg;
397 listen [::]:80 $http2_arg;
398 return 301 https://$server_name$request_uri;
404 if [[ $extra_settings ]]; then
405 cat $extra_settings >>$vhost_file
408 if [[ $proxy ]]; then
409 cat >>$vhost_file <<EOF
411 proxy_set_header Host \$host;
412 proxy_set_header X-Real-IP \$remote_addr;
413 proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
414 proxy_set_header X-Forwarded-Ssl on;
415 proxy_set_header X-Forwarded-Port $port;
416 proxy_pass http://$proxy;
421 cat >>$vhost_file <<EOF
426 service nginx restart
428 fi ####### end if nginx
430 cat >/etc
/apache
2/conf-enabled
/local-custom.conf
<<'EOF'
431 # vhost_combined with %D (request time in microseconds)
432 # this file is just a convenient place to drop it.
433 LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" %D" vhost_time_combined
434 SSLStaplingCache shmcb:/var/run/apache2/stapling_cache(128000)