1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
. ${BUILDFILE%/*}/common.sh
pkgver=20161106.6
package() {
preamble
# Use the [certbot][] ACME client to get certificates from
# [Let's Encrypt][].
#
# [certbot]: https://www.parabola.nu/packages/community/any/certbot/
# [Let's Encrypt]: https://letsencrypt.org/
depends+=(certbot)
# All domains handled by the server are shoved in as Subject
# Alternative Names in a single certificate. This makes configuring
# nginx easier.
# ## issuance, renewal, and installation
# Unlike acmetool, certbot doesn't have an easy way of saying "please
# add this domain as a Subject Alternative Name". You have to re-run
# the same (long) command to get the cert, but with the domain added.
# So, I've encapsulated this into the script
# `/etc/ssl/misc/certbot-get`. Edit `/etc/ssl/misc/certbot-get.d/` to
# manipulate the list of domains, then run the script.
install -d etc/ssl/misc/certbot-get.d
add-file -m755 etc/ssl/misc/certbot-get <<<'#!/bin/bash
{
set -eu
# The first name listed should be the canonical host name
domains=(
$(hostname -f)
$(find -L "$0.d" -type f -executable -exec {} \;)
)
if [[ "`whoami`" != '\''keys'\'' ]]; then
>&2 printf '\''%q: This script must be run as user `%s'\''\'\'''\''\n'\'' "$0" keys
exit 1
fi
msg=(
Our "\`${0##*/}\`" script is used to '\''*add*'\'' or
'\''*remove*'\'' certificates\; use '\''`certbot renew`'\'' to
renew them. To use "${0##*/}," edit "\`${0##*/}.d/\`" to
manipulate the list of domains, '\''then'\'' run it to get a
new certificate with a new Subject Alternative Name field
matching the new list of domains.
$'\''\n\n'\''Are you sure that you are ready to run this?
It will eat into the "Let'\''s Encrypt" usage limit.
)
dialog --yesno "${msg[*]}" '\'''\'' '\'''\'' || { echo; exit 0; }
cmd=(
certbot certonly
--email "`whoami`@${domains[0]}"
--webroot -w /var/lib/letsencrypt
)
for domain in "${domains[@]}"; do
cmd+=(-d "$domain")
done
umask 0027
"${cmd[@]}"
sudo /etc/ssl/misc/certbot-hook
}'
# Renewal, however, is very simple. It is handled by
# `certbot-renew.service` (triggered by the associated `.timer`). It
# runs `certbot renew` with a couple of flags.
add-file etc/systemd/system/certbot-renew.timer <<EOF
[Unit]
Description=Daily renewal of Let's Encrypt's certificates
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
EOF
add-file etc/systemd/system/certbot-renew.service <<EOF
[Unit]
Description=Let's Encrypt certificate renewal
[Service]
Type=oneshot
ExecStart=/usr/bin/certbot renew --quiet --renew-hook 'sudo /etc/ssl/misc/certbot-hook'
User=keys
UMask=0027
EOF
add-unit etc/systemd/system/timers.target.wants/certbot-renew.timer
# Both `certbot-get` and `certbot-renew.serviceq prove ownership of
# the domain via the `http-01` challenge. `/etc/nginx/nginx.conf`
# includes `/etc/nginx/snippets/ssl.conf`, which has a `server{}`
# block that handles ACME http-01 challenges.
add-file etc/nginx/snippets/ssl.conf <<EOF
# -*- Mode: nginx; nginx-indent-level: 8; indent-tabs-mode: t -*-
# This is based on Mozilla Security's recommended web server
# configuration generator[1]:
# Generated date: 2016-06-28
# Server: Nginx
# Clients: Intermediate
# Server Version: 1.10.1
# OpenSSL Version: 1.0.2h
# HSTS Enabled: yes
#
# [1]: https://mozilla.github.io/server-side-tls/ssl-config-generator/
#
# Obviously, all of the '/path/to/' paths have been filled in. The
# 'resolver' line has been commented out. The SSL information has
# been promoted to be in the http{} block directly, instead of having
# to be in each server{} block. The HTTP->HTTPS redirector has had
# ACME support added.
# Redirect everything on port 80 to HTTPS
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
# Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
location / { return 301 https://\$host\$request_uri; }
# Except for ACME http-01 validations
location /.well-known/acme-challenge {
root /var/lib/letsencrypt;
default_type "text/plain";
}
}
# certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
ssl_certificate /etc/ssl/private/myhostname/fullchain.pem;
ssl_certificate_key /etc/ssl/private/myhostname/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
ssl_dhparam /etc/ssl/private/dhparam-2048.pem;
# intermediate configuration. tweak to your needs.
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers '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';
ssl_prefer_server_ciphers on;
# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;
# OCSP Stapling ---
# fetch OCSP records from URL in ssl_certificate and cache them
ssl_stapling on;
ssl_stapling_verify on;
## verify chain of trust of OCSP response using Root CA and Intermediate certs
#ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;
#resolver <IP DNS resolver>;
EOF
# Both `certbot-get` and `certbot-renew.service` have been written to
# run `sudo /etc/ssl/misc/certbot-hook` after certificates have been
# updated, and `sudo` has been configured to allow the keys user to do
# this without a password. Right now `certbot-hook` just runs
# `systemctl reload nginx.service`.
add-file -m755 etc/ssl/misc/certbot-hook <<EOF
#!/bin/bash
systemctl reload nginx.service
EOF
install -dm750 etc/sudoers.d
add-file etc/sudoers.d/10-certbot <<EOF
keys ALL=(ALL) NOPASSWD: /etc/ssl/misc/certbot-hook
EOF
postamble
}
|