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
:i
:f
: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 ;;
70 if (( ${#@} == 3 )); then
71 read -r extra_settings t h
<<<"${@}"
78 *) echo "$0: error: expected apache2 or nginx arg"; usage
1 ;;
82 echo "$0: error: expected domain and type arg"
86 if [[ ! $root ]]; then
91 [[ $proxy == *:* ]] || proxy
=127.0.0.1:$proxy
94 if [[ ! $email ]]; then
95 email
=root@$
(hostname
-A|
awk '{print $1}')
99 ##### end command line parsing ########
101 se
=/etc
/$t/sites-enabled
102 cert_dir
=/etc
/letsencrypt
/live
/$h
105 vhost_file
=$se/$h.conf
106 redir_file
=$se/$h-redir.conf
108 if [[ $port == 80 ]]; then
110 # remove any thats hanging around
116 f
=$cert_dir/fullchain.pem
117 if [[ ! -e $f ]] || openssl x509
-checkend 86400 -noout -in $f; then
119 # when generating an example config, add all relevant security options:
120 # --hsts --staple-ocsp --uir
121 certbot certonly
-n --must-staple --email $email --no-self-upgrade \
122 --agree-tos --${t%2} -d $h
128 if [[ $t == apache2
]]; then
129 rm -f $se/000-default.conf
130 # note, we exepct ServerRoot of /etc/apache2
131 # apache requires exactly 1 listen directive per port (when no ip is also given),
132 # so we have to parse the config to do it programatically.
136 conf_files
=(apache2.conf
)
139 for (( i
=0; i
< ${#conf_files[@]}; i
++ )); do
141 # note: globs are expanded here.
142 conf_files
+=( $
(sed -rn "s,^\s*Include(Optional)?\s+(\S+).*,\2,p" "$f") )
143 case $
(readlink
-f "$f") in
144 $vhost_file|
$redir_file) continue ;;
147 for p
in $
(sed -rn "s,^\s*listen\s+(\S+).*,\1,Ip" "$f"); do
149 80) listen_80
=true
;;&
150 $port) listen_port
=true
;;
156 cat >$vhost_file <<EOF
157 <VirtualHost *:$port>
163 if [[ $extra_settings ]]; then
164 cat -- $extra_settings >>$vhost_file
168 if [[ -e /etc
/apache
2/mods-available
/http2.load
]]; then
169 # https://httpd.apache.org/docs/2.4/mod/mod_http2.html
171 cat >>$vhost_file <<EOF
172 Protocols h2 http/1.1
176 if [[ $proxy ]]; then
177 a2enmod proxy proxy_http
178 # fyi: trailing slash is important
179 # reference: https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html
180 cat >>$vhost_file <<EOF
181 ProxyPass "/" "http://$proxy/"
182 ProxyPassReverse "/" "http://$proxy/"
190 common_ssl_conf
=/etc
/apache
2/common-ssl.conf
191 cat >>$vhost_file <<EOF
192 SSLCertificateFile $cert_dir/fullchain.pem
193 SSLCertificateKeyFile $cert_dir/privkey.pem
194 Include $common_ssl_conf
195 # From cerbot generated config example, taken 4/2017,
196 # should be rechecked once a year or so.
197 Header always set Strict-Transport-Security "max-age=31536000"
199 Header always set Content-Security-Policy upgrade-insecure-requests
202 cat >/etc
/apache
2/conf-enabled
/local-custom.conf
<<'EOF'
203 # vhost_combined with %D (request time in microseconds)
204 # this file is just a convenient place to drop it.
205 LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" %D" vhost_time_combined
206 SSLStaplingCache shmcb:/var/run/apache2/stapling_cache(128000)
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 # Baseline setting to Include for SSL sites
237 # Intermediate configuration, tweak to your needs
238 SSLProtocol all -SSLv2 -SSLv3
239 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
240 SSLHonorCipherOrder on
243 SSLOptions +StrictRequire
245 # Add vhost name to log entries:
246 LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" vhost_combined
247 LogFormat "%v %h %l %u %t \"%r\" %>s %b" vhost_common
249 #CustomLog /var/log/apache2/access.log vhost_combined
251 #ErrorLog /var/log/apache2/error.log
253 # Always ensure Cookies have "Secure" set (JAH 2012/1)
254 #Header edit Set-Cookie (?i)^(.*)(;\s*secure)??((\s*;)?(.*)) "$1; Secure$3$4"
257 upstream
=https
://github.com
/certbot
/certbot
/raw
/master
/certbot-apache
/certbot_apache
/options-ssl-apache.conf
258 if ! diff -c <(wget
-q -O - $upstream) $common_ssl_conf; then
265 upstream ssl settings differ from the snapshot we have taken!!!
266 We diffed with this command:
267 diff -c <(wget -q -O - $upstream) $common_ssl_conf
268 Update this script to take care this warning!!!!!
274 cat >>$vhost_file <<'EOF'
275 ErrorLog ${APACHE_LOG_DIR}/error.log
276 CustomLog ${APACHE_LOG_DIR}/access.log vhost_time_combined
280 if ! $listen_port; then
281 # reference: https://httpd.apache.org/docs/2.4/mod/mpm_common.html#listen
282 cat >>$vhost_file <<EOF
283 listen ${port}${https_arg}
288 a2enmod ssl rewrite
# rewrite needed for httpredir
289 service apache2 restart
291 # I rarely look at how much traffic I get, so let's keep that info
292 # around for longer than the default of 2 weeks.
293 sed -ri --follow-symlinks 's/^(\s*rotate\s).*/\1 365/' /etc
/logrotate.d
/apache2
294 fi ###### end if apache
296 if [[ $t == nginx
]]; then
297 common_ssl_conf
=/etc
/nginx
/common-ssl.conf
301 [[ -e dh2048.pem
]] || openssl dhparam
-out dh2048.pem
2048
305 if nginx
-V |
& grep -- '--with-http_v2_module\b' &>/dev
/null
; then
306 # fun fact: nginx can be configured to do http2 without ssl.
311 cat >$common_ssl_conf <<'EOF'
312 # let's encrypt gives us a bad nginx config, so use this:
313 # https://mozilla.github.io/server-side-tls/ssl-config-generator/
314 # using modern config. last checked 2017/4/22
315 ssl_session_timeout 1d;
316 ssl_session_cache shared:SSL:50m;
317 ssl_session_tickets off;
319 # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
320 ssl_dhparam /etc/nginx/dh2048.pem;
322 # modern configuration. tweak to your needs.
323 ssl_protocols TLSv1.2;
324 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';
325 ssl_prefer_server_ciphers on;
327 # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
328 add_header Strict-Transport-Security max-age=15768000;
331 # fetch OCSP records from URL in ssl_certificate and cache them
333 ssl_stapling_verify on;
335 ## verify chain of trust of OCSP response using Root CA and Intermediate certs
336 # ian: commented out, unnecessary for le certs or my nginx ver.
337 #ssl_trusted_certificate $cert_dir/fullchain.pem;;
339 # ian: commented out, our local dns is expected to work fine.
340 #resolver <IP DNS resolver>;
342 cat >$vhost_file <<EOF
344 server_name $h www.$h;
346 listen $port $ssl_arg;
347 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