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 shopt -s nullglob
# used in apache config file expansion
25 Usage: ${0##*/} [OPTIONS] [EXTRA_SETTINGS_FILE] apache2|nginx DOMAIN
26 apache/nginx config & let's encrypt
28 If using tls then it expects certbot to be installed and in PATH. Also,
29 certbot cronjob should be taken care of outside this script. In the
30 debian package, it installs a systemd timer, which I (Ian Kelling) use
31 and modify to email me on failure. You can see how I do this in my git
32 repo distro-setup, and log-quiet.
35 EXTRA_SETTINGS_FILE can be - for stdin
36 -e EMAIL Contact address for let's encrypt. Default is
37 root@\$(hostname -A|awk '{print $1}')
38 which is root@$(hostname -A|awk '{print $1}') on this host.
39 -f [ADDR:]PORT Enable proxy to [ADDR:]PORT. ADDR default is 127.0.0.1
41 -p PORT Main port to listen on, default 443. 80 implies -i.
43 -h|--help Print help and exit
45 Note: Uses GNU getopt options parsing style
50 ##### begin command line parsing ########
55 temp
=$
(getopt
-l help e
:if:p
:r
:h
"$@") || usage
1
59 -e) email
="$2"; shift 2 ;;
60 -f) proxy
="$2"; shift 2 ;;
61 -i) ssl
=false
; shift ;;
62 -p) port
="$2"; shift 2 ;;
63 -r) root
="$2"; shift 2 ;;
66 *) echo "$0: Internal error!" ; exit 1 ;;
71 if (( ${#@} == 3 )); then
72 read -r extra_settings t h
<<<"${@}"
79 *) echo "$0: error: expected apache2 or nginx arg"; usage
1 ;;
83 echo "$0: error: expected domain and type arg"
87 if [[ ! $root ]]; then
92 [[ $proxy == *:* ]] || proxy
=127.0.0.1:$proxy
95 if [[ ! $email ]]; then
96 email
=root@$
(hostname
-A|
awk '{print $1}')
100 ##### end command line parsing ########
102 se
=/etc
/$t/sites-enabled
103 cert_dir
=/etc
/letsencrypt
/live
/$h
106 vhost_file
=$se/$h.conf
107 redir_file
=$se/$h-redir.conf
109 if [[ $port == 80 ]]; then
111 # remove any thats hanging around
117 f
=$cert_dir/fullchain.pem
118 threedays
=259200 # in seconds
119 if [[ ! -e $f ]] || openssl x509
-checkend $threedays -noout -in $f; then
120 # cerbot needs an existing virtualhost.
122 # when generating an example config, add all relevant security options:
123 # --hsts --staple-ocsp --uir --must-staple
124 certbot certonly
-n --email $email --no-self-upgrade \
125 --agree-tos --${t%2} -d $h
131 if [[ $t == apache2
]]; then
132 rm -f $se/000-default.conf
133 # note, we exepct ServerRoot of /etc/apache2
134 # apache requires exactly 1 listen directive per port (when no ip is also given),
135 # so we have to parse the config to do it programatically.
139 conf_files
=(apache2.conf
)
142 for (( i
=0; i
< ${#conf_files[@]}; i
++ )); do
144 # note: globs are expanded here.
145 conf_files
+=( $
(sed -rn "s,^\s*Include(Optional)?\s+(\S+).*,\2,p" "$f") )
146 case $
(readlink
-f "$f") in
147 $vhost_file|
$redir_file) continue ;;
150 for p
in $
(sed -rn "s,^\s*listen\s+(\S+).*,\1,Ip" "$f"); do
152 80) listen_80
=true
;;&
153 $port) listen_port
=true
;;
159 cat >$vhost_file <<EOF
160 <VirtualHost *:$port>
165 Options -Indexes -FollowSymlinks
169 if [[ $extra_settings ]]; then
170 cat -- $extra_settings >>$vhost_file
174 if [[ -e /etc
/apache
2/mods-available
/http2.load
]]; then
175 # https://httpd.apache.org/docs/2.4/mod/mod_http2.html
177 cat >>$vhost_file <<EOF
178 Protocols h2 http/1.1
182 if [[ $proxy ]]; then
183 a2enmod proxy proxy_http
184 # fyi: trailing slash is important
185 # reference: https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html
186 # retry=0: https://stackoverflow.com/questions/683052/why-am-i-getting-an-apache-proxy-503-error
187 cat >>$vhost_file <<EOF
188 ProxyPass "/" "http://$proxy/" retry=0
189 ProxyPassReverse "/" "http://$proxy/"
197 common_ssl_conf
=/etc
/apache
2/common-ssl.conf
198 cat >>$vhost_file <<EOF
199 SSLCertificateFile $cert_dir/fullchain.pem
200 SSLCertificateKeyFile $cert_dir/privkey.pem
201 Include $common_ssl_conf
202 # From cerbot generated config example, taken 4/2017,
203 # should be rechecked once a year or so.
204 Header always set Strict-Transport-Security "max-age=31536000"
206 Header always set Content-Security-Policy upgrade-insecure-requests
209 echo "$0: creating $redir_file"
210 cat >$redir_file <<EOF
213 ServerAdmin webmaster@localhost
214 DocumentRoot /var/www/html
216 ErrorLog \${APACHE_LOG_DIR}/error.log
217 CustomLog \${APACHE_LOG_DIR}/access.log vhost_time_combined
220 RewriteCond %{SERVER_NAME} =$h
221 RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]
224 if ! $listen_80; then
225 cat >>$redir_file <<'EOF'
230 # this is a copy of a file certbot, see below.
231 echo "$0: creating $common_ssl_conf"
232 cat >$common_ssl_conf <<'EOF'
233 # This file contains important security parameters. If you modify this file
234 # manually, Certbot will be unable to automatically provide future security
235 # updates. Instead, Certbot will print and log an error message with a path to
236 # the up-to-date file that you will need to refer to when manually updating
241 # Intermediate configuration, tweak to your needs
242 SSLProtocol all -SSLv2 -SSLv3
243 SSLCipherSuite ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS
244 SSLHonorCipherOrder on
246 SSLSessionTickets off
248 SSLOptions +StrictRequire
250 # Add vhost name to log entries:
251 LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" vhost_combined
252 LogFormat "%v %h %l %u %t \"%r\" %>s %b" vhost_common
255 upstream
=https
://raw.githubusercontent.com
/certbot
/certbot
/master
/certbot-apache
/certbot_apache
/options-ssl-apache.conf
256 if ! diff -u <(wget
-q -O - $upstream) $common_ssl_conf; then
263 upstream ssl settings differ from the snapshot we have taken!!!
264 We diffed with this command:
265 diff -c <(wget -q -O - $upstream) $common_ssl_conf
266 Update this script to take care this warning!!!!!
272 cat >>$vhost_file <<'EOF'
273 ErrorLog ${APACHE_LOG_DIR}/error.log
274 CustomLog ${APACHE_LOG_DIR}/access.log vhost_time_combined
278 if ! $listen_port; then
279 # reference: https://httpd.apache.org/docs/2.4/mod/mpm_common.html#listen
280 cat >>$vhost_file <<EOF
281 listen ${port}${https_arg}
286 a2enmod ssl rewrite
# rewrite needed for httpredir
287 service apache2 restart
289 # I rarely look at how much traffic I get, so let's keep that info
290 # around for longer than the default of 2 weeks.
291 sed -ri --follow-symlinks 's/^(\s*rotate\s).*/\1 365/' /etc
/logrotate.d
/apache2
292 fi ###### end if apache
294 if [[ $t == nginx
]]; then
295 common_ssl_conf
=/etc
/nginx
/common-ssl.conf
299 [[ -e dh2048.pem
]] || openssl dhparam
-out dh2048.pem
2048
303 if nginx
-V |
& grep -- '--with-http_v2_module\b' &>/dev
/null
; then
304 # fun fact: nginx can be configured to do http2 without ssl.
309 cat >$common_ssl_conf <<'EOF'
310 # let's encrypt gives us a bad nginx config, so use this:
311 # https://mozilla.github.io/server-side-tls/ssl-config-generator/
312 # using modern config. last checked 2017/4/22
313 ssl_session_timeout 1d;
314 ssl_session_cache shared:SSL:50m;
315 ssl_session_tickets off;
317 # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
318 ssl_dhparam /etc/nginx/dh2048.pem;
320 # modern configuration. tweak to your needs.
321 ssl_protocols TLSv1.2;
322 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';
323 ssl_prefer_server_ciphers on;
325 # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
326 add_header Strict-Transport-Security max-age=15768000;
329 # fetch OCSP records from URL in ssl_certificate and cache them
331 ssl_stapling_verify on;
333 ## verify chain of trust of OCSP response using Root CA and Intermediate certs
334 # ian: commented out, unnecessary for le certs or my nginx ver.
335 #ssl_trusted_certificate $cert_dir/fullchain.pem;;
337 # ian: commented out, our local dns is expected to work fine.
338 #resolver <IP DNS resolver>;
340 cat >$vhost_file <<EOF
342 server_name $h www.$h;
344 listen $port $ssl_arg;
345 listen [::]:$port $ssl_arg;
351 cat >>$vhost_file <<EOF
352 ssl_certificate $cert_dir/fullchain.pem;
353 ssl_certificate_key $cert_dir/privkey.pem;
354 include $common_ssl_conf;
357 cat >$redir_file <<EOF
359 server_name $h www.$h;
360 listen 80 $http2_arg;
361 listen [::]:80 $http2_arg;
362 return 301 https://$server_name$request_uri;
367 if [[ $extra_settings ]]; then
368 cat $extra_settings >>$vhost_file
371 if [[ $proxy ]]; then
372 cat >>$vhost_file <<EOF
374 proxy_set_header Host \$host;
375 proxy_set_header X-Real-IP \$remote_addr;
376 proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
377 proxy_set_header X-Forwarded-Ssl on;
378 proxy_set_header X-Forwarded-Port $port;
379 proxy_pass http://$proxy;
384 cat >>$vhost_file <<EOF
389 service nginx restart
391 fi ####### end if nginx
393 cat >/etc
/apache
2/conf-enabled
/local-custom.conf
<<'EOF'
394 # vhost_combined with %D (request time in microseconds)
395 # this file is just a convenient place to drop it.
396 LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" %D" vhost_time_combined
397 SSLStaplingCache shmcb:/var/run/apache2/stapling_cache(128000)