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 ;;
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
118 # cerbot needs an existing virtualhost.
120 # when generating an example config, add all relevant security options:
121 # --hsts --staple-ocsp --uir
122 certbot certonly
-n --must-staple --email $email --no-self-upgrade \
123 --agree-tos --${t%2} -d $h
129 if [[ $t == apache2
]]; then
130 rm -f $se/000-default.conf
131 # note, we exepct ServerRoot of /etc/apache2
132 # apache requires exactly 1 listen directive per port (when no ip is also given),
133 # so we have to parse the config to do it programatically.
137 conf_files
=(apache2.conf
)
140 for (( i
=0; i
< ${#conf_files[@]}; i
++ )); do
142 # note: globs are expanded here.
143 conf_files
+=( $
(sed -rn "s,^\s*Include(Optional)?\s+(\S+).*,\2,p" "$f") )
144 case $
(readlink
-f "$f") in
145 $vhost_file|
$redir_file) continue ;;
148 for p
in $
(sed -rn "s,^\s*listen\s+(\S+).*,\1,Ip" "$f"); do
150 80) listen_80
=true
;;&
151 $port) listen_port
=true
;;
157 cat >$vhost_file <<EOF
158 <VirtualHost *:$port>
164 if [[ $extra_settings ]]; then
165 cat -- $extra_settings >>$vhost_file
169 if [[ -e /etc
/apache
2/mods-available
/http2.load
]]; then
170 # https://httpd.apache.org/docs/2.4/mod/mod_http2.html
172 cat >>$vhost_file <<EOF
173 Protocols h2 http/1.1
177 if [[ $proxy ]]; then
178 a2enmod proxy proxy_http
179 # fyi: trailing slash is important
180 # reference: https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html
181 cat >>$vhost_file <<EOF
182 ProxyPass "/" "http://$proxy/"
183 ProxyPassReverse "/" "http://$proxy/"
191 common_ssl_conf
=/etc
/apache
2/common-ssl.conf
192 cat >>$vhost_file <<EOF
193 SSLCertificateFile $cert_dir/fullchain.pem
194 SSLCertificateKeyFile $cert_dir/privkey.pem
195 Include $common_ssl_conf
196 # From cerbot generated config example, taken 4/2017,
197 # should be rechecked once a year or so.
198 Header always set Strict-Transport-Security "max-age=31536000"
200 Header always set Content-Security-Policy upgrade-insecure-requests
203 cat >/etc
/apache
2/conf-enabled
/local-custom.conf
<<'EOF'
204 # vhost_combined with %D (request time in microseconds)
205 # this file is just a convenient place to drop it.
206 LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" %D" vhost_time_combined
207 SSLStaplingCache shmcb:/var/run/apache2/stapling_cache(128000)
210 echo "$0: creating $redir_file"
211 cat >$redir_file <<EOF
214 ServerAdmin webmaster@localhost
215 DocumentRoot /var/www/html
217 ErrorLog \${APACHE_LOG_DIR}/error.log
218 CustomLog \${APACHE_LOG_DIR}/access.log vhost_time_combined
221 RewriteCond %{SERVER_NAME} =$h
222 RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]
225 if ! $listen_80; then
226 cat >>$redir_file <<'EOF'
231 # this is a copy of a file certbot, see below.
232 echo "$0: creating $common_ssl_conf"
233 cat >$common_ssl_conf <<'EOF'
234 # Baseline setting to Include for SSL sites
238 # Intermediate configuration, tweak to your needs
239 SSLProtocol all -SSLv2 -SSLv3
240 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
241 SSLHonorCipherOrder on
244 SSLOptions +StrictRequire
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
250 #CustomLog /var/log/apache2/access.log vhost_combined
252 #ErrorLog /var/log/apache2/error.log
254 # Always ensure Cookies have "Secure" set (JAH 2012/1)
255 #Header edit Set-Cookie (?i)^(.*)(;\s*secure)??((\s*;)?(.*)) "$1; Secure$3$4"
258 upstream
=https
://github.com
/certbot
/certbot
/raw
/master
/certbot-apache
/certbot_apache
/options-ssl-apache.conf
259 if ! diff -c <(wget
-q -O - $upstream) $common_ssl_conf; then
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!!!!!
275 cat >>$vhost_file <<'EOF'
276 ErrorLog ${APACHE_LOG_DIR}/error.log
277 CustomLog ${APACHE_LOG_DIR}/access.log vhost_time_combined
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}
289 a2enmod ssl rewrite
# rewrite needed for httpredir
290 service apache2 restart
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
297 if [[ $t == nginx
]]; then
298 common_ssl_conf
=/etc
/nginx
/common-ssl.conf
302 [[ -e dh2048.pem
]] || openssl dhparam
-out dh2048.pem
2048
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.
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;
320 # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
321 ssl_dhparam /etc/nginx/dh2048.pem;
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;
328 # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
329 add_header Strict-Transport-Security max-age=15768000;
332 # fetch OCSP records from URL in ssl_certificate and cache them
334 ssl_stapling_verify on;
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;;
340 # ian: commented out, our local dns is expected to work fine.
341 #resolver <IP DNS resolver>;
343 cat >$vhost_file <<EOF
345 server_name $h www.$h;
347 listen $port $ssl_arg;
348 listen [::]:$port $ssl_arg;
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;
358 cat >$redir_file <<EOF
360 server_name $h www.$h;
361 listen 80 $http2_arg;
362 listen [::]:80 $http2_arg;
363 return 301 https://$server_name$request_uri;
368 if [[ $extra_settings ]]; then
369 cat $extra_settings >>$vhost_file
372 if [[ $proxy ]]; then
373 cat >>$vhost_file <<EOF
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;
385 cat >>$vhost_file <<EOF
390 service nginx restart
392 fi ####### end if nginx