update ciphers from upstream, small changes
[basic-https-conf] / web-conf
1 #!/bin/bash
2 # Copyright (C) 2016 Ian Kelling
3
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
7
8 # http://www.apache.org/licenses/LICENSE-2.0
9
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.
15
16 [[ $EUID == 0 ]] || exec sudo -E "$BASH_SOURCE" "$@"
17
18 set -eE -o pipefail
19 trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
20
21 shopt -s nullglob # used in apache config file expansion
22
23 usage() {
24 cat <<EOF
25 Usage: ${0##*/} [OPTIONS] [EXTRA_SETTINGS_FILE] apache2|nginx DOMAIN
26 apache/nginx config & let's encrypt
27
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.
33
34
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
40 -i Insecure, no ssl.
41 -p PORT Main port to listen on, default 443. 80 implies -i.
42 -r DIR DocumentRoot
43 -h|--help Print help and exit
44
45 Note: Uses GNU getopt options parsing style
46 EOF
47 exit $1
48 }
49
50 ##### begin command line parsing ########
51
52 ssl=true
53 extra_settings=
54 port=443
55 temp=$(getopt -l help e:if:p:r:h "$@") || usage 1
56 eval set -- "$temp"
57 while true; do
58 case $1 in
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 ;;
64 --) shift; break ;;
65 -h|--help) usage ;;
66 *) echo "$0: Internal error!" ; exit 1 ;;
67 esac
68 done
69
70 # t = type, h = host
71 if (( ${#@} == 3 )); then
72 read -r extra_settings t h <<<"${@}"
73 else
74 read -r t h <<<"${@}"
75 fi
76
77 case $t in
78 apache2|nginx) : ;;
79 *) echo "$0: error: expected apache2 or nginx arg"; usage 1 ;;
80 esac
81
82 if [[ ! $h ]]; then
83 echo "$0: error: expected domain and type arg"
84 usage 1
85 fi
86
87 if [[ ! $root ]]; then
88 root=/var/www/$h/html
89 fi
90
91 if [[ $proxy ]]; then
92 [[ $proxy == *:* ]] || proxy=127.0.0.1:$proxy
93 fi
94
95 if [[ ! $email ]]; then
96 email=root@$(hostname -A|awk '{print $1}')
97 fi
98
99
100 ##### end command line parsing ########
101
102 se=/etc/$t/sites-enabled
103 cert_dir=/etc/letsencrypt/live/$h
104
105 mkdir -p $root
106 vhost_file=$se/$h.conf
107 redir_file=$se/$h-redir.conf
108
109 if [[ $port == 80 ]]; then
110 ssl=false
111 # remove any thats hanging around
112 rm -f $redir_file
113 fi
114
115
116 if $ssl; then
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.
121 $0 -p 80 $t $h
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
126 rm $vhost_file
127 fi
128 fi
129
130
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.
136 listen_80=false
137 listen_port=false
138 cd /etc/apache2
139 conf_files=(apache2.conf)
140
141
142 for (( i=0; i < ${#conf_files[@]}; i++ )); do
143 f="${conf_files[i]}"
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 ;;
148 esac
149 echo "$f"
150 for p in $(sed -rn "s,^\s*listen\s+(\S+).*,\1,Ip" "$f"); do
151 case $p in
152 80) listen_80=true ;;&
153 $port) listen_port=true ;;
154 esac
155 done
156 done
157
158
159 cat >$vhost_file <<EOF
160 <VirtualHost *:$port>
161 ServerName $h
162 ServerAlias www.$h
163 DocumentRoot $root
164 EOF
165
166 if [[ $extra_settings ]]; then
167 cat -- $extra_settings >>$vhost_file
168 fi
169
170 # go faster!
171 if [[ -e /etc/apache2/mods-available/http2.load ]]; then
172 # https://httpd.apache.org/docs/2.4/mod/mod_http2.html
173 a2enmod http2
174 cat >>$vhost_file <<EOF
175 Protocols h2 http/1.1
176 EOF
177 fi
178
179 if [[ $proxy ]]; then
180 a2enmod proxy proxy_http
181 # fyi: trailing slash is important
182 # reference: https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html
183 # retry=0: https://stackoverflow.com/questions/683052/why-am-i-getting-an-apache-proxy-503-error
184 cat >>$vhost_file <<EOF
185 ProxyPass "/" "http://$proxy/" retry=0
186 ProxyPassReverse "/" "http://$proxy/"
187 EOF
188 fi
189
190
191 if $ssl; then
192 a2enmod headers
193 https_arg=" https"
194 common_ssl_conf=/etc/apache2/common-ssl.conf
195 cat >>$vhost_file <<EOF
196 SSLCertificateFile $cert_dir/fullchain.pem
197 SSLCertificateKeyFile $cert_dir/privkey.pem
198 Include $common_ssl_conf
199 # From cerbot generated config example, taken 4/2017,
200 # should be rechecked once a year or so.
201 Header always set Strict-Transport-Security "max-age=31536000"
202 SSLUseStapling on
203 Header always set Content-Security-Policy upgrade-insecure-requests
204 EOF
205
206 echo "$0: creating $redir_file"
207 cat >$redir_file <<EOF
208 <VirtualHost *:80>
209 ServerName $h
210 ServerAdmin webmaster@localhost
211 DocumentRoot /var/www/html
212
213 ErrorLog \${APACHE_LOG_DIR}/error.log
214 CustomLog \${APACHE_LOG_DIR}/access.log vhost_time_combined
215
216 RewriteEngine on
217 RewriteCond %{SERVER_NAME} =$h
218 RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]
219 </VirtualHost>
220 EOF
221 if ! $listen_80; then
222 cat >>$redir_file <<'EOF'
223 Listen 80
224 EOF
225 fi
226
227 # this is a copy of a file certbot, see below.
228 echo "$0: creating $common_ssl_conf"
229 cat >$common_ssl_conf <<'EOF'
230 # This file contains important security parameters. If you modify this file
231 # manually, Certbot will be unable to automatically provide future security
232 # updates. Instead, Certbot will print and log an error message with a path to
233 # the up-to-date file that you will need to refer to when manually updating
234 # this file.
235
236 SSLEngine on
237
238 # Intermediate configuration, tweak to your needs
239 SSLProtocol all -SSLv2 -SSLv3
240 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
241 SSLHonorCipherOrder on
242 SSLCompression off
243
244 SSLOptions +StrictRequire
245
246 # Add vhost name to log entries:
247 LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" vhost_combined
248 LogFormat "%v %h %l %u %t \"%r\" %>s %b" vhost_common
249
250 #CustomLog /var/log/apache2/access.log vhost_combined
251 #LogLevel warn
252 #ErrorLog /var/log/apache2/error.log
253
254 # Always ensure Cookies have "Secure" set (JAH 2012/1)
255 #Header edit Set-Cookie (?i)^(.*)(;\s*secure)??((\s*;)?(.*)) "$1; Secure$3$4"
256 EOF
257
258 upstream=https://github.com/certbot/certbot/raw/master/certbot-apache/certbot_apache/options-ssl-apache.conf
259 if ! diff -u <(wget -q -O - $upstream) $common_ssl_conf; then
260 cat <<EOF
261 WARNING!!!!!!!!!
262 WARNING!!!!!!!!!
263 WARNING!!!!!!!!!
264 WARNING!!!!!!!!!
265 WARNING!!!!!!!!!
266 upstream ssl settings differ from the snapshot we have taken!!!
267 We diffed with this command:
268 diff -c <(wget -q -O - $upstream) $common_ssl_conf
269 Update this script to take care this warning!!!!!
270 EOF
271 sleep 1
272 fi
273 fi # end if $ssl
274
275 cat >>$vhost_file <<'EOF'
276 ErrorLog ${APACHE_LOG_DIR}/error.log
277 CustomLog ${APACHE_LOG_DIR}/access.log vhost_time_combined
278 </VirtualHost>
279 EOF
280
281 if ! $listen_port; then
282 # reference: https://httpd.apache.org/docs/2.4/mod/mpm_common.html#listen
283 cat >>$vhost_file <<EOF
284 listen ${port}${https_arg}
285 EOF
286 fi
287
288
289 a2enmod ssl rewrite # rewrite needed for httpredir
290 service apache2 restart
291
292 # I rarely look at how much traffic I get, so let's keep that info
293 # around for longer than the default of 2 weeks.
294 sed -ri --follow-symlinks 's/^(\s*rotate\s).*/\1 365/' /etc/logrotate.d/apache2
295 fi ###### end if apache
296
297 if [[ $t == nginx ]]; then
298 common_ssl_conf=/etc/nginx/common-ssl.conf
299
300 rm -f $se/default
301 cd /etc/nginx
302 [[ -e dh2048.pem ]] || openssl dhparam -out dh2048.pem 2048
303
304 if $ssl; then
305 ssl_arg=ssl
306 if nginx -V |& grep -- '--with-http_v2_module\b' &>/dev/null; then
307 # fun fact: nginx can be configured to do http2 without ssl.
308 ssl_arg+=" http2"
309 fi
310 fi
311
312 cat >$common_ssl_conf <<'EOF'
313 # let's encrypt gives us a bad nginx config, so use this:
314 # https://mozilla.github.io/server-side-tls/ssl-config-generator/
315 # using modern config. last checked 2017/4/22
316 ssl_session_timeout 1d;
317 ssl_session_cache shared:SSL:50m;
318 ssl_session_tickets off;
319
320 # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
321 ssl_dhparam /etc/nginx/dh2048.pem;
322
323 # modern configuration. tweak to your needs.
324 ssl_protocols TLSv1.2;
325 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';
326 ssl_prefer_server_ciphers on;
327
328 # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
329 add_header Strict-Transport-Security max-age=15768000;
330
331 # OCSP Stapling ---
332 # fetch OCSP records from URL in ssl_certificate and cache them
333 ssl_stapling on;
334 ssl_stapling_verify on;
335
336 ## verify chain of trust of OCSP response using Root CA and Intermediate certs
337 # ian: commented out, unnecessary for le certs or my nginx ver.
338 #ssl_trusted_certificate $cert_dir/fullchain.pem;;
339
340 # ian: commented out, our local dns is expected to work fine.
341 #resolver <IP DNS resolver>;
342 EOF
343 cat >$vhost_file <<EOF
344 server {
345 server_name $h www.$h;
346 root $root;
347 listen $port $ssl_arg;
348 listen [::]:$port $ssl_arg;
349
350 EOF
351 if $ssl; then
352 cat >>$vhost_file <<EOF
353 ssl_certificate $cert_dir/fullchain.pem;
354 ssl_certificate_key $cert_dir/privkey.pem;
355 include $common_ssl_conf;
356 EOF
357
358 cat >$redir_file <<EOF
359 server {
360 server_name $h www.$h;
361 listen 80 $http2_arg;
362 listen [::]:80 $http2_arg;
363 return 301 https://$server_name$request_uri;
364 }
365 EOF
366 fi # end if $ssl
367
368 if [[ $extra_settings ]]; then
369 cat $extra_settings >>$vhost_file
370 fi
371
372 if [[ $proxy ]]; then
373 cat >>$vhost_file <<EOF
374 location / {
375 proxy_set_header Host \$host;
376 proxy_set_header X-Real-IP \$remote_addr;
377 proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
378 proxy_set_header X-Forwarded-Ssl on;
379 proxy_set_header X-Forwarded-Port $port;
380 proxy_pass http://$proxy;
381 }
382 EOF
383 fi
384
385 cat >>$vhost_file <<EOF
386 }
387 EOF
388
389
390 service nginx restart
391
392 fi ####### end if nginx
393
394 cat >/etc/apache2/conf-enabled/local-custom.conf <<'EOF'
395 # vhost_combined with %D (request time in microseconds)
396 # this file is just a convenient place to drop it.
397 LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" %D" vhost_time_combined
398 SSLStaplingCache shmcb:/var/run/apache2/stapling_cache(128000)
399 EOF