-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathinstall-wks-server
executable file
·293 lines (237 loc) · 8.31 KB
/
install-wks-server
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
#!/bin/bash
# A QAD script to set up a new wks server. It takes one mandatory argument,
# the DOMAIN for which wks will be configured. It takes an optional second
# argument, which is the email address of the system administrator (defaults
# to "webmaster@$DOMAIN") for use in registering the server with letsencrypt,
# if it has not already been done.
# You MUST already have an MTA configured and able to deliver to local users.
# You MUST have incoming TCP ports 80 and 443 enabled.
# You MUST have a DNS record for "openpgpkey.$DOMAIN" pointing to this machine.
set -euo pipefail
err_report() {
echo "errexit on line $(caller)" >&2
}
trap err_report ERR
die() {
echo $2 >&2
exit $1
}
[[ ${1-} ]] || die 1 "You must provide a domain name"
# Support headless installation
export DEBIAN_FRONTEND=noninteractive
export NEEDRESTART_SUSPEND=y
DOMAIN="${1-}"
LETSENCRYPT_EMAIL="${2-webmaster@$DOMAIN}"
SERVER_NAME="openpgpkey.$DOMAIN"
WKSUSER=key-submission
SUBMISSION_ADDRESS="$WKSUSER@$SERVER_NAME"
WKSHOME=/var/lib/gnupg/wks
WEBROOT="$WKSHOME/html"
DEFAULT_GPG_OPTIONS="--pinentry-mode loopback --keyid-format 0xlong --with-fingerprint --status-fd 2"
apt install gpg-wks-server apache2 certbot procmail
APACHEUSER=$(ps aux | grep /usr/sbin/apache2 | grep -v ^root | awk '{print $1}' | sort -u)
APACHEGROUP=$(eval echo $(groups $APACHEUSER|awk -F: '{print $2}'))
# Before we do anything else, make sure we have enough entropy
# https://lists.gnupg.org/pipermail/gnupg-users/2020-March/063372.html
mkdir -p /etc/gcrypt && echo only-urandom>/etc/gcrypt/random.conf
#################################
###### Set up service user ######
#################################
# create user
mkdir -p $(dirname "$WKSHOME")
adduser --disabled-password --home "$WKSHOME" --gecos "GNUPG WKS Server" "$WKSUSER"
mkdir -p "$WKSHOME/$DOMAIN" "$WKSHOME/Mail" "$WEBROOT"
# configure mail delivery
cat <<'EOF' >"$WKSHOME/.forward"
"|exec /usr/bin/procmail || exit 75"
EOF
cat <<EOF >"$WKSHOME/.procmailrc"
MAILDIR=\$HOME/Mail
LOGFILE=\$HOME/Mail/from
LOCKFILE=\$HOME/Mail/.lockmail
VERBOSE=yes
# filter out FROM_DAEMON mails (bounces, ...) into separate mailbox
:0
* ^FROM_DAEMON
/dev/null
# archive (copy!) all "normal" mails
:0 c
archive/
# if not in a loop: handle mails with gpg-wks-server
:0 w
* !^From: $SUBMISSION_ADDRESS
* !^X-WKS-Loop: $DOMAIN
|gpg-wks-server -v --receive \
--header X-WKS-Loop=$DOMAIN \
--from $SUBMISSION_ADDRESS --send
# if handling failed: store in separate mailbox
:0 e
cruft/
EOF
chown -R "$WKSUSER:$WKSUSER" "$WKSHOME"
chown "$WKSUSER:$APACHEGROUP" "$WKSHOME"
chown -R "$WKSUSER:$APACHEGROUP" "$WKSHOME/$DOMAIN" "$WEBROOT"
chmod -R o= "$WKSHOME"
chmod -R g-w "$WKSHOME"
# configure submission address
echo "$SUBMISSION_ADDRESS" > "$WKSHOME/$DOMAIN/submission-address"
# initialise the WKS directory structure
sudo -Hu "$WKSUSER" gpg-wks-server --list-domains
# create and publish pgp key for submission address
sudo -Hu "$WKSUSER" gpg $DEFAULT_GPG_OPTIONS --batch --passphrase '' --quick-gen-key "$SUBMISSION_ADDRESS"
HASH=$( \
sudo -Hu $WKSUSER gpg $DEFAULT_GPG_OPTIONS --with-wkd-hash -K "$SUBMISSION_ADDRESS" \
| perl -ne "print if s/^\s+([a-zA-Z0-9]+)\@$SERVER_NAME$/\$1/" \
)
sudo -Hu "$WKSUSER" gpg $DEFAULT_GPG_OPTIONS -o "/var/lib/gnupg/wks/$DOMAIN/hu/$HASH" \
--export-options export-minimal --export "$SUBMISSION_ADDRESS"
touch "$WKSHOME/$DOMAIN/policy"
# daily grooming cron job
cat <<EOF >/etc/cron.d/gpg-wks-server
42 3 * * * $WKSUSER gpg-wks-server --cron
EOF
###############################
###### Set up web server ######
###############################
cat > "/etc/apache2/sites-available/$SERVER_NAME.conf" <<EOF
<VirtualHost *:80>
ServerName $SERVER_NAME
DocumentRoot $WEBROOT
DirectoryIndex index.html index.htm
CustomLog /var/log/apache2/$SERVER_NAME.access.log combined
ErrorLog /var/log/apache2/$SERVER_NAME.error.log
<Directory $WEBROOT>
Options -Indexes
AllowOverride None
Require all granted
</Directory>
# redirect everything else to SSL
RedirectMatch 301 ^(?!/\.well-known/acme-challenge/).* https://$SERVER_NAME\$0
</VirtualHost>
EOF
a2ensite "$SERVER_NAME"
if ! apache2ctl configtest >/dev/null; then
exit 2
fi
# Remove the 000-default vhost if it exists and our server name is the fqdn.
# Otherwise apache will have undefined behaviour.
if [[ -L /etc/apache2/sites-enabled/000-default.conf && $(hostname --fqdn) == "$SERVER_NAME" ]]; then
a2dissite 000-default
fi
apache2ctl graceful
# Get a letsencrypt certificate
# Register with letsencrypt if we don't already have an account locally
if ! ls /etc/letsencrypt/accounts/*.api.letsencrypt.org/directory/*/meta.json 2>&1 >/dev/null; then
yes a | certbot register --email "$LETSENCRYPT_EMAIL" --no-eff-email || true
fi
certbot certonly -d "$SERVER_NAME" -n --keep --webroot --webroot-path "$WEBROOT" || exit 3
if [[ ! -x /etc/letsencrypt/renewal-hooks/deploy/50-webservers ]]; then
cat <<EOF > /etc/letsencrypt/renewal-hooks/deploy/50-webservers
#!/bin/bash
set -e
err_report() {
echo "errexit on line \$(caller)" >&2
}
trap err_report ERR
if [[ \$RENEWED_DOMAINS ]]; then
for service in apache2 nginx; do
if systemctl status \$service | grep -q '(running)'; then
service \$service reload >/dev/null
fi
done
fi
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/50-webservers
fi
cat >> "/etc/apache2/sites-available/$SERVER_NAME.conf" <<EOF
<VirtualHost *:443>
ServerName $SERVER_NAME
DocumentRoot $WEBROOT
DirectoryIndex index.html index.htm
CustomLog /var/log/apache2/$SERVER_NAME-ssl-request.log "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
CustomLog /var/log/apache2/$SERVER_NAME-ssl.access.log combined
ErrorLog /var/log/apache2/$SERVER_NAME-ssl.error.log
<Directory $WEBROOT>
Options Indexes
AllowOverride None
Require all granted
</Directory>
<Directory $WKSHOME/$DOMAIN>
Options -Indexes
AllowOverride None
Require all granted
</Directory>
SSLEngine on
SSLStrictSNIVHostCheck off
SSLCertificateFile /etc/letsencrypt/live/$SERVER_NAME/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/$SERVER_NAME/privkey.pem
SSLCACertificateFile /etc/letsencrypt/live/$SERVER_NAME/chain.pem
# Allow WKD lookups from JavaScript webapps
Header set Access-Control-Allow-Origin "*"
# More specific alias comes first
Alias "/.well-known/openpgpkey/$DOMAIN" "$WKSHOME/$DOMAIN"
Alias "/.well-known/openpgpkey" "$WKSHOME/$DOMAIN"
</VirtualHost>
EOF
cat <<'EOF' >/etc/apache2/conf-available/security.conf
ServerTokens Prod
ServerSignature Off
TraceEnable Off
<IfModule mod_headers.c>
Header always merge X-Content-Type-Options "nosniff"
Header always merge X-Frame-Options "sameorigin"
Header always unset X-Powered-By
Header always edit Set-Cookie ^(.*)$ ";HttpOnly;Secure"
</IfModule>
<DirectoryMatch "^\.git|\/\.git">
Deny from all
Satisfy all
</DirectoryMatch>
EOF
cat <<'EOF' >/etc/apache2/mods-available/ssl.conf
<IfModule mod_ssl.c>
SSLRandomSeed startup builtin
SSLRandomSeed startup file:/dev/urandom 512
SSLRandomSeed connect builtin
SSLRandomSeed connect file:/dev/urandom 512
AddType application/x-x509-ca-cert .crt
AddType application/x-pkcs7-crl .crl
SSLPassPhraseDialog exec:/usr/share/apache2/ask-for-passphrase
SSLSessionCache shmcb:${APACHE_RUN_DIR}/ssl_scache(512000)
SSLSessionCacheTimeout 300
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLHonorCipherOrder on
# DO NOT USE TABS IN SSLCipherSuite
SSLCipherSuite \
"EECDH+AESGCM EECDH EDH+AESGCM EDH !SEED !SHA1 !SHA256 !SHA384 !MEDIUM !LOW !aNULL !eNULL !PSK"
SSLCompression off
## Strict Transport Security
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=15768000"
</IfModule>
## Apache 2.4 only
SSLUseStapling on
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
SSLStaplingCache shmcb:/var/run/ocsp(128000)
## Apache >=2.4.8 + OpenSSL >=1.0.2 only
SSLOpenSSLConfCmd DHParameters /etc/ssl/certs/dhparam.pem
</IfModule>
EOF
if [[ ! -e /etc/ssl/certs/dhparam.pem ]]; then
openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
fi
a2enconf security
a2enmod ssl
a2enmod headers
apache2ctl graceful
cat <<EOF >"$WEBROOT/index.html"
<html>
<head>
<title>OpenPGP WKD server</title>
</head>
<body>
<p>This is an OpenPGP WKD server.</p>
</body>
</html>
EOF