08d1c047f2bd649da80745acc075af18359a10a2
[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.
29
30
31 EXTRA_SETTINGS_FILE can be - for stdin
32 -e EMAIL Contact address for let's encrypt. Default is
33 root@\$(hostname -A|awk '{print $1}')
34 which is root@$(hostname -A|awk '{print $1}') on this host.
35 -f [ADDR:]PORT Enable proxy to [ADDR:]PORT. ADDR default is 127.0.0.1
36 -i Insecure, no ssl.
37 -p PORT Main port to listen on, default 443. 80 implies -i.
38 -r DIR DocumentRoot
39 -h|--help Print help and exit
40
41 Note: Uses GNU getopt options parsing style
42 EOF
43 exit $1
44 }
45
46 ##### begin command line parsing ########
47
48 ssl=true
49 extra_settings=
50 port=443
51 temp=$(getopt -l help e:i:f:p:r:h "$@") || usage 1
52 eval set -- "$temp"
53 while true; do
54 case $1 in
55 -e) email="$2"; shift 2 ;;
56 -f) proxy="$2"; shift 2 ;;
57 -i) ssl=false; shift ;;
58 -p) port="$2"; shift 2 ;;
59 -r) root="$2"; shift 2 ;;
60 --) shift; break ;;
61 -h|--help) usage ;;
62 *) echo "$0: Internal error!" ; exit 1 ;;
63 esac
64 done
65
66 if (( ${#@} == 3 )); then
67 read -r extra_settings t h <<<"${@}"
68 else
69 read -r t h <<<"${@}"
70 fi
71
72 case $t in
73 apache2|nginx) : ;;
74 *) echo "$0: error: expected apache2 or nginx arg"; usage 1 ;;
75 esac
76
77 if [[ ! $h ]]; then
78 echo "$0: error: expected domain and type arg"
79 usage 1
80 fi
81
82 if [[ ! $root ]]; then
83 root=/var/www/$h/html
84 fi
85
86 if [[ $proxy ]]; then
87 [[ $proxy == *:* ]] || proxy=127.0.0.1:$proxy
88 fi
89
90 if [[ ! $email ]]; then
91 email=root@$(hostname -A|awk '{print $1}')
92 fi
93
94
95 ##### end command line parsing ########
96
97 se=/etc/$t/sites-enabled
98 cert_dir=/etc/letsencrypt/live/$h
99
100 mkdir -p $root
101 vhost_file=$se/$h.conf
102 redir_file=$se/$h-redir.conf
103
104 if [[ $port == 80 ]]; then
105 ssl=false
106 # remove any thats hanging around
107 rm -f $redir_file
108 fi
109
110
111 if $ssl; then
112 f=$cert_dir/fullchain.pem
113 if [[ ! -e $f ]] || openssl x509 -checkend 86400 -noout -in $f; then
114 $0 -p 80 $t $h
115 # when generating an example config, add all relevant security options:
116 # --hsts --staple-ocsp --uir
117 certbot certonly -n --must-staple --email $email --no-self-upgrade \
118 --agree-tos --$t -d $h
119 rm $vhost_file
120 fi
121 fi
122
123
124 if [[ $t == apache2 ]]; then
125 rm -f $se/000-default.conf
126 # note, we exepct ServerRoot of /etc/apache2
127 # apache requires exactly 1 listen directive per port (when no ip is also given),
128 # so we have to parse the config to do it programatically.
129 listen_80=false
130 listen_port=false
131 cd /etc/apache2
132 conf_files=(apache2.conf)
133
134
135 for (( i=0; i < ${#conf_files[@]}; i++ )); do
136 f="${conf_files[i]}"
137 # note: globs are expanded here.
138 conf_files+=( $(sed -rn "s,^\s*Include(Optional)?\s+(\S+).*,\2,p" "$f") )
139 case $(readlink -f "$f") in
140 $vhost_file|$redir_file) continue ;;
141 esac
142 echo "$f"
143 for p in $(sed -rn "s,^\s*listen\s+(\S+).*,\1,Ip" "$f"); do
144 case $p in
145 80) listen_80=true ;;&
146 $port) listen_port=true ;;
147 esac
148 done
149 done
150
151
152 cat >$vhost_file <<EOF
153 <VirtualHost *:$port>
154 ServerName $h
155 ServerAlias www.$h
156 DocumentRoot $root
157 EOF
158
159 if [[ $extra_settings ]]; then
160 cat -- $extra_settings >>$vhost_file
161 fi
162
163 # go faster!
164 if [[ -e /etc/apache2/mods-available/http2.load ]]; then
165 # https://httpd.apache.org/docs/2.4/mod/mod_http2.html
166 a2enmod http2
167 cat >>$vhost_file <<EOF
168 Protocols h2 http/1.1
169 EOF
170 fi
171
172 if [[ $proxy ]]; then
173 a2enmod proxy proxy_http
174 # fyi: trailing slash is important
175 # reference: https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html
176 cat >>$vhost_file <<EOF
177 ProxyPass "/" "http://$proxy/"
178 ProxyPassReverse "/" "http://$proxy/"
179 EOF
180 fi
181
182
183
184 if $ssl; then
185 https_arg=" https"
186 common_ssl_conf=/etc/apache2/common-ssl.conf
187 cat >>$vhost_file <<EOF
188 SSLCertificateFile $cert_dir/fullchain.pem
189 SSLCertificateKeyFile $cert_dir/privkey.pem
190 Include $common_ssl_conf
191 # From cerbot generated config example, taken 4/2017,
192 # should be rechecked once a year or so.
193 Header always set Strict-Transport-Security "max-age=31536000"
194 SSLUseStapling on
195 Header always set Content-Security-Policy upgrade-insecure-requests
196 EOF
197
198 cat >/etc/apache2/conf-enabled/local-custom.conf <<'EOF'
199 # vhost_combined with %D (request time in microseconds)
200 # this file is just a convenient place to drop it.
201 LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" %D" vhost_time_combined
202 SSLStaplingCache shmcb:/var/run/apache2/stapling_cache(128000)
203 EOF
204
205 echo "$0: creating $redir_file"
206 cat >$redir_file <<EOF
207 <VirtualHost *:80>
208 ServerName $h
209 ServerAdmin webmaster@localhost
210 DocumentRoot /var/www/html
211
212 ErrorLog \${APACHE_LOG_DIR}/error.log
213 CustomLog \${APACHE_LOG_DIR}/access.log vhost_time_combined
214
215 RewriteEngine on
216 RewriteCond %{SERVER_NAME} =$h
217 RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]
218 </VirtualHost>
219 EOF
220 if ! $listen_80; then
221 cat >>$redir_file <<'EOF'
222 Listen 80
223 EOF
224 fi
225
226 # this is a copy of a file certbot, see below.
227 echo "$0: creating $common_ssl_conf"
228 cat >$common_ssl_conf <<'EOF'
229 # Baseline setting to Include for SSL sites
230
231 SSLEngine on
232
233 # Intermediate configuration, tweak to your needs
234 SSLProtocol all -SSLv2 -SSLv3
235 SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
236 SSLHonorCipherOrder on
237 SSLCompression off
238
239 SSLOptions +StrictRequire
240
241 # Add vhost name to log entries:
242 LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" vhost_combined
243 LogFormat "%v %h %l %u %t \"%r\" %>s %b" vhost_common
244
245 #CustomLog /var/log/apache2/access.log vhost_combined
246 #LogLevel warn
247 #ErrorLog /var/log/apache2/error.log
248
249 # Always ensure Cookies have "Secure" set (JAH 2012/1)
250 #Header edit Set-Cookie (?i)^(.*)(;\s*secure)??((\s*;)?(.*)) "$1; Secure$3$4"
251 EOF
252
253 upstream=https://github.com/certbot/certbot/raw/master/certbot-apache/certbot_apache/options-ssl-apache.conf
254 if ! diff -c <(wget -q -O - $upstream) $common_ssl_conf; then
255 cat <<EOF
256 WARNING!!!!!!!!!
257 WARNING!!!!!!!!!
258 WARNING!!!!!!!!!
259 WARNING!!!!!!!!!
260 WARNING!!!!!!!!!
261 upstream ssl settings differ from the snapshot we have taken!!!
262 We diffed with this command:
263 diff -c <(wget -q -O - $upstream) $common_ssl_conf
264 Update this script to take care this warning!!!!!
265 EOF
266 sleep 1
267 fi
268 fi # end if $ssl
269
270 cat >>$vhost_file <<'EOF'
271 ErrorLog ${APACHE_LOG_DIR}/error.log
272 CustomLog ${APACHE_LOG_DIR}/access.log vhost_time_combined
273 </VirtualHost>
274 EOF
275
276 if ! $listen_port; then
277 # reference: https://httpd.apache.org/docs/2.4/mod/mpm_common.html#listen
278 cat >>$vhost_file <<EOF
279 listen ${port}${https_arg}
280 EOF
281 fi
282
283
284 a2enmod ssl rewrite # rewrite needed for httpredir
285 service apache2 restart
286
287 # I rarely look at how much traffic I get, so let's keep that info
288 # around for longer than the default of 2 weeks.
289 sed -ri --follow-symlinks 's/^(\s*rotate\s).*/\1 365/' /etc/logrotate.d/apache2
290 fi ###### end if apache
291
292 if [[ $t == nginx ]]; then
293 common_ssl_conf=/etc/nginx/common-ssl.conf
294
295 rm -f $se/default
296 cd /etc/nginx
297 [[ -e dh2048.pem ]] || openssl dhparam -out dh2048.pem 2048
298
299 if $ssl; then
300 ssl_arg=ssl
301 if nginx -V |& grep -- '--with-http_v2_module\b' &>/dev/null; then
302 # fun fact: nginx can be configured to do http2 without ssl.
303 ssl_arg+=" http2"
304 fi
305 fi
306
307 cat >$common_ssl_conf <<'EOF'
308 # let's encrypt gives us a bad nginx config, so use this:
309 # https://mozilla.github.io/server-side-tls/ssl-config-generator/
310 # using modern config. last checked 2017/4/22
311 ssl_session_timeout 1d;
312 ssl_session_cache shared:SSL:50m;
313 ssl_session_tickets off;
314
315 # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
316 ssl_dhparam /etc/nginx/dh2048.pem;
317
318 # modern configuration. tweak to your needs.
319 ssl_protocols TLSv1.2;
320 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';
321 ssl_prefer_server_ciphers on;
322
323 # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
324 add_header Strict-Transport-Security max-age=15768000;
325
326 # OCSP Stapling ---
327 # fetch OCSP records from URL in ssl_certificate and cache them
328 ssl_stapling on;
329 ssl_stapling_verify on;
330
331 ## verify chain of trust of OCSP response using Root CA and Intermediate certs
332 # ian: commented out, unnecessary for le certs or my nginx ver.
333 #ssl_trusted_certificate $cert_dir/fullchain.pem;;
334
335 # ian: commented out, our local dns is expected to work fine.
336 #resolver <IP DNS resolver>;
337 EOF
338 cat >$vhost_file <<EOF
339 server {
340 server_name $h www.$h;
341 root $root;
342 listen $port $ssl_arg;
343 listen [::]:$port $ssl_arg;
344
345 EOF
346 if $ssl; then
347 cat >>$vhost_file <<EOF
348 ssl_certificate $cert_dir/fullchain.pem;
349 ssl_certificate_key $cert_dir/privkey.pem;
350 include $common_ssl_conf;
351 EOF
352
353 cat >$redir_file <<EOF
354 server {
355 server_name $h www.$h;
356 listen 80 $http2_arg;
357 listen [::]:80 $http2_arg;
358 return 301 https://$server_name$request_uri;
359 }
360 EOF
361 fi # end if $ssl
362
363 if [[ $extra_settings ]]; then
364 cat $extra_settings >>$vhost_file
365 fi
366
367 if [[ $proxy ]]; then
368 cat >>$vhost_file <<EOF
369 location / {
370 proxy_set_header Host \$host;
371 proxy_set_header X-Real-IP \$remote_addr;
372 proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
373 proxy_set_header X-Forwarded-Ssl on;
374 proxy_set_header X-Forwarded-Port $port;
375 proxy_pass http://$proxy;
376 }
377 EOF
378 fi
379
380 cat >>$vhost_file <<EOF
381 }
382 EOF
383
384
385 service nginx restart
386
387 fi ####### end if nginx