postfix-tls-script revision 1.1 1 #!/bin/sh
2 # $NetBSD: postfix-tls-script,v 1.1 2017/02/14 01:13:34 christos Exp $
3 #
4
5 #++
6 # NAME
7 # postfix-tls 1
8 # SUMMARY
9 # Postfix TLS management
10 # SYNOPSIS
11 # \fBpostfix tls\fR \fIsubcommand\fR
12 # DESCRIPTION
13 # The "\fBpostfix tls \fIsubcommand\fR" feature enables
14 # opportunistic TLS in the Postfix SMTP client or server, and
15 # manages Postfix SMTP server private keys and certificates.
16 #
17 # The following subcommands are available:
18 # .IP "\fBenable-client\fR [\fB-r \fIrandsource\fR]"
19 # Enable opportunistic TLS in the Postfix SMTP client, if all
20 # SMTP client TLS settings are at their default values.
21 # Otherwise, suggest parameter settings without making any
22 # changes.
23 # .sp
24 # Specify \fIrandsource\fR to update the value of the
25 # \fBtls_random_source\fR configuration parameter (typically,
26 # /dev/urandom). Prepend \fBdev:\fR to device paths or
27 # \fBegd:\fR to EGD socket paths.
28 # .sp
29 # See also the \fBall-default-client\fR subcommand.
30 # .IP "\fBenable-server\fR [\fB-r \fIrandsource\fR] [\fB-a \fIalgorithm\fR] [\fB-b \fIbits\fR] [\fIhostname\fB...\fR]"
31 # Create a new private key and self-signed server certificate
32 # and enable opportunistic TLS in the Postfix SMTP server,
33 # if all SMTP server TLS settings are at their default values.
34 # Otherwise, suggest parameter settings without making any
35 # changes.
36 # .sp
37 # The \fIrandsource\fR parameter is as with \fBenable-client\fR
38 # above, and the remaining options are as with \fBnew-server-key\fR
39 # below.
40 # .sp
41 # See also the \fBall-default-server\fR subcommand.
42 # .IP "\fBnew-server-key\fR [\fB-a \fIalgorithm\fR] [\fB-b \fIbits\fR] [\fIhostname\fB...\fR]"
43 # Create a new private key and self-signed server certificate,
44 # but do not deploy them. Log and display commands to deploy
45 # the new key and corresponding certificate. Also log and
46 # display commands to output a corresponding CSR or TLSA
47 # records which may be needed to obtain a CA certificate or
48 # to update DNS before the new key can be deployed.
49 # .sp
50 # The \fIalgorithm\fR defaults to \fBrsa\fR, and \fIbits\fR
51 # defaults to 2048. If you choose the \fBecdsa\fR \fIalgorithm\fR
52 # then \fIbits\fR will be an EC curve name (by default
53 # \fBsecp256r1\fR, also known as prime256v1). Curves other
54 # than \fBsecp256r1\fR, \fBsecp384r1\fR or \fBsecp521r1\fR
55 # are unlikely to be widely interoperable. When generating
56 # EC keys, use one of these three. DSA keys are obsolete and
57 # are not supported.
58 # .sp
59 # Note: ECDSA support requires OpenSSL 1.0.0 or later and may
60 # not be available on your system. Not all client systems
61 # will support ECDSA, so you'll generally want to deploy both
62 # RSA and ECDSA certificates to make use of ECDSA with
63 # compatible clients and RSA with the rest. If you want to
64 # deploy certificate chains with intermediate CAs for both
65 # RSA and ECDSA, you'll want at least OpenSSL 1.0.2, as earlier
66 # versions may not handle multiple chain files correctly.
67 # .sp
68 # The first \fIhostname\fR argument will be the \fBCommonName\fR
69 # of both the subject and issuer of the self-signed certificate.
70 # It, and any additional \fIhostname\fR arguments, will also
71 # be listed as DNS alternative names in the certificate. If
72 # no \fIhostname\fR is provided the value of the \fBmyhostname\fR
73 # main.cf parameter will be used.
74 # .sp
75 # For RSA, the generated private key and certificate files
76 # are named \fBkey-\fIyyyymmdd-hhmmss\fB.pem\fR and
77 # \fBcert-\fIyyyymmdd-hhmmss\fB.pem\fR, where \fIyyyymmdd\fR
78 # is the calendar date and \fIhhmmss\fR is the time of day
79 # in UTC. For ECDSA, the file names start with \fBeckey-\fR
80 # and \fBeccert-\fR instead of \fBkey-\fR and \fBcert-\fR
81 # respectively.
82 # .sp
83 # Before deploying the new key and certificate with DANE,
84 # update the DNS with new DANE TLSA records, then wait for
85 # secondary nameservers to update and then for stale records
86 # in remote DNS caches to expire.
87 # .sp
88 # Before deploying a new CA certificate make sure to include
89 # all the required intermediate issuing CA certificates in
90 # the certificate chain file. The server certificate must
91 # be the first certificate in the chain file. Overwrite and
92 # deploy the file with the original self-signed certificate
93 # that was generated together with the key.
94 # .IP "\fBnew-server-cert\fR [\fB-a \fIalgorithm\fR] [\fB-b \fIbits\fR] [\fIhostname\fB...\fR]"
95 # This is just like \fBnew-server-key\fR except that, rather
96 # than generating a new private key, any currently deployed
97 # private key is copied to the new key file. Thus if you're
98 # publishing DANE TLSA "3 1 1" or "3 1 2" records, there is
99 # no need to update DNS records. The \fIalgorithm\fR and
100 # \fIbits\fR arguments are used only if no key of the same
101 # algorithm is already configured.
102 # .sp
103 # This command is rarely needed, because the self-signed
104 # certificates generated have a 100-year nominal expiration
105 # time. The underlying public key algorithms may well be
106 # obsoleted by quantum computers long before then.
107 # .sp
108 # The most plausible reason for using this command is when
109 # the system hostname changes, and you'd like the name in the
110 # certificate to match the new hostname (not required for
111 # DANE "3 1 1", but some needlessly picky non-DANE opportunistic
112 # TLS clients may log warnings or even refuse to communicate).
113 # .IP "\fBdeploy-server-cert \fIcertfile\fB \fIkeyfile\fR"
114 # This subcommand deploys the certificates in \fIcertfile\fR
115 # and private key in \fIkeyfile\fR (which are typically
116 # generated by the commands above, which will also log and
117 # display the full command needed to deploy the generated key
118 # and certificate). After the new certificate and key are
119 # deployed any obsolete keys and certificates may be removed
120 # by hand. The \fIkeyfile\fR and \fIcertfile\fR filenames
121 # may be relative to the Postfix configuration directory.
122 # .IP "\fBoutput-server-csr\fR [\fB-k \fIkeyfile\fR] [\fIhostname\fB...\fR]"
123 # Write to stdout a certificate signing request (CSR) for the
124 # specified \fIkeyfile\fR.
125 # .sp
126 # Instead of an absolute pathname or a pathname relative to
127 # $config_directory, \fIkeyfile\fR may specify one of the
128 # supported key algorithm names (see "\fBpostconf -T
129 # public-key-algorithms\fR"). In that case, the corresponding
130 # setting from main.cf is used to locate the \fIkeyfile\fR.
131 # The default \fIkeyfile\fR value is \fBrsa\fR.
132 # .sp
133 # Zero or more \fIhostname\fR values can be specified. The
134 # default \fIhostname\fR is the value of \fBmyhostname\fR
135 # main.cf parameter.
136 # .IP "\fBoutput-server-tlsa\fR [\fB-h \fIhostname\fR] [\fIkeyfile\fB...\fR]"
137 # Write to stdout a DANE TLSA RRset suitable for a port 25
138 # SMTP server on host \fIhostname\fR with keys from any of
139 # the specified \fIkeyfile\fR values. The default \fIhostname\fR
140 # is the value of the \fBmyhostname\fR main.cf parameter.
141 # .sp
142 # Instead of absolute pathnames or pathnames relative to
143 # $config_directory, the \fIkeyfile\fR list may specify
144 # names of supported public key algorithms (see "\fBpostconf
145 # -T public-key-algorithms\fR"). In that case, the actual
146 # \fIkeyfile\fR list uses the values of the corresponding
147 # Postfix server TLS key file parameters. If a parameter
148 # value is empty or equal to \fBnone\fR, then no TLSA record
149 # is output for that algorithm.
150 # .sp
151 # The default \fIkeyfile\fR list consists of the two supported
152 # algorithms \fBrsa\fR and \fBecdsa\fR.
153 # AUXILIARY COMMANDS
154 # .IP "\fBall-default-client\fR"
155 # Exit with status 0 (success) if all SMTP client TLS settings are
156 # at their default values. Otherwise, exit with a non-zero status.
157 # This is typically used as follows:
158 # .sp
159 # \fBpostfix tls all-default-client &&
160 # postfix tls enable-client\fR
161 # .IP "\fBall-default-server\fR"
162 # Exit with status 0 (success) if all SMTP server TLS settings are
163 # at their default values. Otherwise, exit with a non-zero status.
164 # This is typically used as follows:
165 # .sp
166 # \fBpostfix tls all-default-server &&
167 # postfix tls enable-server\fR
168 # CONFIGURATION PARAMETERS
169 # .ad
170 # .fi
171 # The "\fBpostfix tls \fIsubcommand\fR" feature reads
172 # or updates the following configuration parameters.
173 # .IP "\fBcommand_directory (see 'postconf -d' output)\fR"
174 # The location of all postfix administrative commands.
175 # .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
176 # The default location of the Postfix main.cf and master.cf
177 # configuration files.
178 # .IP "\fBopenssl_path (openssl)\fR"
179 # The location of the OpenSSL command line program \fBopenssl\fR(1).
180 # .IP "\fBsmtp_tls_loglevel (0)\fR"
181 # Enable additional Postfix SMTP client logging of TLS activity.
182 # .IP "\fBsmtp_tls_security_level (empty)\fR"
183 # The default SMTP TLS security level for the Postfix SMTP client;
184 # when a non-empty value is specified, this overrides the obsolete
185 # parameters smtp_use_tls, smtp_enforce_tls, and smtp_tls_enforce_peername.
186 # .IP "\fBsmtp_tls_session_cache_database (empty)\fR"
187 # Name of the file containing the optional Postfix SMTP client
188 # TLS session cache.
189 # .IP "\fBsmtpd_tls_cert_file (empty)\fR"
190 # File with the Postfix SMTP server RSA certificate in PEM format.
191 # .IP "\fBsmtpd_tls_eccert_file (empty)\fR"
192 # File with the Postfix SMTP server ECDSA certificate in PEM format.
193 # .IP "\fBsmtpd_tls_eckey_file ($smtpd_tls_eccert_file)\fR"
194 # File with the Postfix SMTP server ECDSA private key in PEM format.
195 # .IP "\fBsmtpd_tls_key_file ($smtpd_tls_cert_file)\fR"
196 # File with the Postfix SMTP server RSA private key in PEM format.
197 # .IP "\fBsmtpd_tls_loglevel (0)\fR"
198 # Enable additional Postfix SMTP server logging of TLS activity.
199 # .IP "\fBsmtpd_tls_received_header (no)\fR"
200 # Request that the Postfix SMTP server produces Received: message
201 # headers that include information about the protocol and cipher used,
202 # as well as the remote SMTP client CommonName and client certificate issuer
203 # CommonName.
204 # .IP "\fBsmtpd_tls_security_level (empty)\fR"
205 # The SMTP TLS security level for the Postfix SMTP server; when
206 # a non-empty value is specified, this overrides the obsolete parameters
207 # smtpd_use_tls and smtpd_enforce_tls.
208 # .IP "\fBtls_random_source (see 'postconf -d' output)\fR"
209 # The external entropy source for the in-memory \fBtlsmgr\fR(8) pseudo
210 # random number generator (PRNG) pool.
211 # SEE ALSO
212 # master(8) Postfix master program
213 # postfix(1) Postfix administrative interface
214 # README FILES
215 # .ad
216 # .fi
217 # Use "\fBpostconf readme_directory\fR" or
218 # "\fBpostconf html_directory\fR" to locate this information.
219 # .na
220 # .nf
221 # TLS_README, Postfix TLS configuration and operation
222 # LICENSE
223 # .ad
224 # .fi
225 # The Secure Mailer license must be distributed with this software.
226 # HISTORY
227 # The "\fBpostfix tls\fR" command was introduced with Postfix
228 # version 3.1.
229 # AUTHOR(S)
230 # Viktor Dukhovni
231 #--
232
233 RSA_BITS=2048 # default
234 EC_CURVE=secp256r1 # default
235
236 case $daemon_directory in
237 "") echo This script must be run by the postfix command. 1>&2
238 echo Do not run directly. 1>&2
239 exit 1;;
240 esac
241
242 umask 022
243 SHELL=/bin/sh
244
245 postconf=$command_directory/postconf
246 LOGGER="$command_directory/postlog -t $MAIL_LOGTAG/postfix-tls-script"
247 INFO="$LOGGER -p info"
248 WARN="$LOGGER -p warn"
249 ERROR="$LOGGER -p error"
250 FATAL="$LOGGER -p fatal"
251
252 # Overwrite SMTP client and server settings only when these are at defaults.
253 client_settings="
254 smtp_use_tls
255 smtp_enforce_tls
256 smtp_tls_enforce_peername
257 smtp_tls_security_level
258 smtp_tls_cert_file
259 smtp_tls_dcert_file
260 smtp_tls_eccert_file
261 "
262
263 server_settings="
264 smtpd_use_tls
265 smtpd_enforce_tls
266 smtpd_tls_security_level
267 smtpd_tls_cert_file
268 smtpd_tls_dcert_file
269 smtpd_tls_eccert_file
270 "
271
272 #
273 # Can't do much without these in place.
274 #
275 cd $command_directory || {
276 # Let's hope there's a "postlog" somewhere else on the PATH
277 FATAL="postlog -p fatal -t $MAIL_LOGTAG/postfix-tls-script"
278 msg="no Postfix command directory '${command_directory}'"
279 $FATAL "$msg" || { echo "$msg" >&2; sleep 1; }
280 exit 1
281 }
282
283 check_getopt() {
284 OPTIND=1
285 a=
286 b=
287 c=
288 set -- -a 1 -b 2 -c -- -pos
289 while getopts :a:b:c o
290 do
291 case $o in
292 a) a="${OPTARG}";;
293 b) b="${OPTARG}";;
294 c) c=3;;
295 *) return 1;;
296 esac
297 done
298 shift `expr ${OPTIND} - 1`
299 if [ "${a}" != "1" -o "${b}" != 2 -o "${c}" != 3 \
300 -o "${OPTIND}" -ne 7 -o "$1" != "-pos" ]; then
301 return 1
302 fi
303 }
304
305 check_getopt || {
306 $FATAL "/bin/sh does not implement a compatible 'getopts' built-in"
307 exit 1
308 }
309
310 # ----- BEGIN OpenSSL-specific -----
311
312 # No need to set the location of the OpenSSL command in each Postfix instance,
313 # the value from the default instance is used for all instances.
314 #
315 default_config_directory=`$postconf -dh config_directory`
316 openssl=`$postconf -c $default_config_directory -xh openssl_path`
317 "$openssl" version >/dev/null 2>&1 || {
318 $FATAL "No working openssl(1) command found with 'openssl_path = $openssl'"
319 exit 1
320 }
321
322 # ----- END OpenSSL-specific -----
323
324 test -n "$config_directory" -a -d "$config_directory" || {
325 $FATAL no Postfix configuration directory $config_directory!
326 exit 1
327 }
328
329 # Do we support TLS and if so which algorithms?
330 #
331 $postconf -T compile-version | grep . >/dev/null || {
332 mail_version=`$postconf -dh mail_version`
333 $FATAL "Postfix $mail_version is not compiled with TLS support"
334 exit 1
335 }
336 rsa=
337 ecdsa=
338 for _algo in `$postconf -T public-key-algorithms | egrep '^(rsa|ecdsa)$'`
339 do
340 eval $_algo=$_algo
341 done
342
343 # ----- BEGIN OpenSSL-specific -----
344
345 if [ -n "${ecdsa}" ]; then
346 $openssl ecparam -name secp256r1 >/dev/null 2>&1 || {
347 cat <<-EOM | $WARN
348 Postfix supports ECDSA, but the $openssl command does not. Consider
349 setting the openssl_path parameter to a more capable version of the
350 command-line utility than $openssl (with PATH=$PATH).
351 EOM
352 ecdsa=
353 }
354 fi
355 if [ -n "${rsa}" ]; then
356 DEFALG=rsa
357 elif [ -n "${ecdsa}" ]; then
358 DEFALG=ecdsa
359 else
360 mail_version=`$postconf -dh mail_version`
361 $FATAL "Postfix $mail_version does not support either RSA or ECDSA"
362 exit 1
363 fi
364
365 # Make sure stdin is open when testing
366 if [ -r /dev/stdin ] < /dev/null; then
367 stdin=/dev/stdin
368 elif [ -r /dev/fd/0 ] </dev/null; then
369 stdin=/dev/fd/0
370 else
371 $FATAL No /dev/fd/0 or /dev/stdin found
372 exit 1
373 fi
374
375 hex_sha256() {
376 $openssl dgst -binary -sha256 | od -An -vtx1 | tr -d ' \012'
377 }
378
379 # We require SHA2-256 support from openssl(1)
380 #
381 null256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
382 tmp=`hex_sha256 </dev/null 2>/dev/null`
383 if [ "${tmp}" != "${null256}" ]; then
384 cat <<EOF >&2
385 Your $openssl does not support the SHA2-256 digest algorithm. To enable
386 'postfix tls', install an OpenSSL that does. Install its openssl(1) command
387 at /usr/local/bin/openssl or other suitable location, and set the
388 'openssl_path' parameter in $default_config_directory/main.cf accordingly.
389 EOF
390 $FATAL "No 'postfix tls' support when openssl(1) is obsolete"
391 exit 1
392 fi
393
394 read_key() {
395 [ -n "$1" -a -f "$1" ] || return 1
396
397 # Old OpenSSL versions return success even for unsupported sub-commands!
398 # So we inspect the output instead. Don't prompt if the key is password
399 # protected.
400 #
401 while read cmd key_algo key_param cert_param; do
402 $openssl $cmd -passin "pass:umask 077" -in "$1" |
403 grep . && return 0
404 done 2>/dev/null <<-EOF
405 rsa rsa smtpd_tls_key_file smtpd_tls_cert_file
406 ec ecdsa smtpd_tls_eckey_file smtpd_tls_eccert_file
407 EOF
408 return 1
409 }
410
411 pubkey_dgst() {
412 [ -n "$1" -a -f "$1" ] || return 1
413
414 # Old OpenSSL versions return success even for unsupported sub-commands!
415 # So we inspect the output instead.
416 #
417 for cmd in ec rsa; do
418 $openssl $cmd -passin "pass:umask 077" -in "$1" -pubout |
419 $openssl $cmd -pubin -outform DER |
420 hex_sha256 | egrep -v "${null256}" && return 0
421 done 2>/dev/null
422 return 1
423 }
424
425 cert_pubkey_dgst() {
426 [ -n "$1" -a -f "$1" ] || return 1
427
428 # Old OpenSSL versions return success even for unsupported sub-commands!
429 # So we inspect the output instead.
430 #
431 for cmd in ec rsa; do
432 $openssl x509 -pubkey -noout -in "$1" |
433 $openssl $cmd -pubin -outform DER |
434 hex_sha256 | egrep -v "${null256}" && return 0
435 done 2>/dev/null
436 return 1
437 }
438
439 copy_key() {
440 _algo=$1; shift
441 _bits=$1; shift
442 _fold=$1; shift
443 _fnew=$1; shift
444 _umask=`umask`
445
446 umask 077
447 read_key "${_fold}" > "${_fnew}" # sets key_algo of current key
448 _ret=$?
449 umask "${_umask}"
450
451 if [ "${_ret}" -ne 0 ]; then
452 $FATAL "Error copying private key from '${_fold}' to '${_fnew}'"
453 return 1
454 fi
455 if [ "${key_algo}" != "${_algo}" ]; then
456 $FATAL "Key algorithm '$key_algo' of '${_fold}' is not '${_algo}'"
457 return 1
458 fi
459 # XXX: We'd need C-code in postconf to portably check for compatible "bits"
460 }
461
462 create_key() {
463 _algo=$1
464 _bits=$2
465 _fnew=$3
466 _umask=`umask`
467
468 case $_algo in
469 "") $FATAL "Internal error: empty algorithm"; return 1;;
470 $rsa) set -- "${openssl}" genrsa -out "${_fnew}" "${_bits}";;
471 $ecdsa) set -- "${openssl}" ecparam -param_enc named_curve -genkey \
472 -out "${_fnew}" -name "${_bits}";;
473 *) $FATAL "Internal error: bad algorithm '${_algo}'"
474 return 1;;
475 esac
476
477 umask 077
478 _err=`"$@" 2>&1`
479 _ret=$?
480 umask "${_umask}"
481
482 if [ "${_ret}" -ne 0 ]; then
483 echo "${_err}" | $WARN
484 $FATAL "error generating new ${_algo} ${_bits} private key"
485 return 1
486 fi
487 }
488
489 create_cert() {
490 _k=$1; shift
491 _c=$1; shift
492 set_fqdn "$1"
493 if [ $# -gt 0 ]; then shift; fi
494 set -- "$fqdn" "$@"
495
496 if [ -r "${_c}" ]; then
497 $FATAL "New certificate file already exists: ${_c}"
498 return 1
499 fi
500
501 # Generate a new self-signed (~100 year) certificate
502 #
503 (
504 echo "default_md = sha256"
505 echo "x509_extensions = v3"
506 echo "prompt = yes"
507 echo "distinguished_name = dn"
508 echo "[dn]"
509 echo "[v3]"
510 echo "basicConstraints = CA:false"
511 echo "subjectKeyIdentifier = hash"
512 echo "extendedKeyUsage = serverAuth, clientAuth"
513 echo "subjectAltName = @alts"
514 echo "[alts]"
515 i=1; for dns in "$@"; do
516 # XXX map empty to $myhostname
517 echo "DNS.$i = $dns"
518 i=`expr $i + 1`
519 done
520 ) | $openssl req -x509 -config $stdin -new -key "${_k}" \
521 -subj "/CN=$fqdn" -days 36525 -out "${_c}" || {
522 rm -f "${_c}" "${_k}"
523 $FATAL "error generating self-signed SSL certificate"
524 return 1
525 }
526 }
527
528 output_server_csr() {
529 set_keyfile "$1" || return 1
530 shift
531 set_fqdn "$1" || return 1
532 shift
533 set -- "$fqdn" "$@"
534 (
535 echo "default_md = sha256"
536 echo "req_extensions = v3"
537 echo "prompt = yes"
538 echo "distinguished_name = dn"
539 echo "[dn]"
540 echo "[v3]"
541 echo "subjectKeyIdentifier = hash"
542 echo "extendedKeyUsage = serverAuth, clientAuth"
543 echo "subjectAltName = @alts"
544 echo "[alts]"
545 i=1; for dns in "$@"; do
546 echo "DNS.$i = $dns"
547 i=`expr $i + 1`
548 done
549 ) | $openssl req -config $stdin -new -key "$keyfile" -subj /
550 }
551
552 # ----- END OpenSSL-specific -----
553
554 info_enable_client() {
555 cat <<-EOM
556 *** Non-default SMTP client TLS settings detected, no changes made.
557 For opportunistic TLS in the Postfix SMTP client, the below settings
558 are typical:
559 smtp_tls_security_level = may
560 smtp_tls_loglevel = 1
561 EOM
562 if get_cache_db_type dbtype
563 then
564 echo " smtp_tls_session_cache_database = ${dbtype}:\${data_directory}/smtp_scache"
565 fi
566 }
567
568 info_client_deployed() {
569 cat <<-EOM
570 Enabled opportunistic TLS in the Postfix SMTP client.
571 Run the command:
572 # postfix reload
573 if you want the new settings to take effect immediately.
574 EOM
575 }
576
577 info_enable_server() {
578 cat <<-EOM
579 *** Non-default SMTP server TLS settings detected, no changes made.
580 For opportunistic TLS in the Postfix SMTP server, the below settings
581 are typical:
582 smtpd_tls_security_level = may
583 smtpd_tls_loglevel = 1
584 You can use "postfix tls new-server-cert" to create a new certificate.
585 Or, "postfix tls new-server-key" to also force a new private key.
586 If you publish DANE TLSA records, see:
587 https://tools.ietf.org/html/rfc7671#section-8
588 https://tools.ietf.org/html/rfc7671#section-5.1
589 https://tools.ietf.org/html/rfc7671#section-5.2
590 https://community.letsencrypt.org/t/please-avoid-3-0-1-and-3-0-2-dane-tlsa-records-with-le-certificates/7022
591 EOM
592 }
593
594 # args: certfile keyfile deploy
595 info_created() {
596 cat <<-EOM
597 New private key and self-signed certificate created. To deploy run:
598 # postfix tls deploy-server-cert $1 $2
599 EOM
600 }
601
602 # args: certfile keyfile deploy
603 info_server_deployed() {
604 if [ "$3" = "enable" ]; then
605 echo "Enabled opportunistic TLS in the Postfix SMTP server"
606 fi
607 cat <<-EOM
608 New TLS private key and certificate deployed.
609 Run the command:
610 # postfix reload
611 if you want the new settings to take effect immediately.
612 EOM
613 }
614
615 # args: certfile keyfile deploy
616 info_csr() {
617 cat <<-EOM
618 To generate a CSR run:
619 # postfix tls output-server-csr -k $2 [<hostname> ...]
620 EOM
621 if [ -z "$3" ]; then
622 echo "Save the signed certificate chain in $1, and deploy as above."
623 else
624 echo "Save the signed certificate chain in $1."
625 fi
626 }
627
628 # args: certfile keyfile deploy
629 info_tlsa() {
630 # If already deployed, info for how to show all the deployed keys.
631 # Otherwise, just the new keys, so that TLSA records can be updated
632 # first.
633 if [ -n "$3" ]; then shift $#; fi
634 cat <<-EOM
635 To generate TLSA records run:
636 # postfix tls output-server-tlsa [-h <hostname>] $2
637 EOM
638 }
639
640 # args: certfile keyfile deploy
641 info_dane_dns() {
642 # If already deployed, too late to wait, otherwise advise updating TLSA
643 # RRs before deployment.
644 if [ -n "$3" ]; then
645 cat <<-EOM
646 (If you have DANE TLSA RRs, update them as soon as possible to match
647 the newly deployed keys).
648 EOM
649 else
650 cat <<-EOM
651 (deploy after updating the DNS and waiting for stale RRs to expire).
652 EOM
653 fi
654 }
655
656 set_fqdn() {
657 if [ -n "$1" ]; then fqdn=$1; return 0; fi
658 fqdn=`$postconf -xh myhostname` || return 1
659 case $fqdn in /*) fqdn=`cat "${fqdn}"` || return 1;; esac
660 }
661
662 set_keyfile() {
663 keyfile=$1
664 case $keyfile in
665 rsa) if [ -n "${rsa}" ]; then
666 keyfile=`$postconf -nxh smtpd_tls_key_file`
667 else
668 keyfile=
669 fi
670 ;;
671 ecdsa) if [ -n "${ecdsa}" ]; then
672 keyfile=`$postconf -nxh smtpd_tls_eckey_file`
673 else
674 keyfile=
675 fi
676 ;;
677 "") : empty ok;;
678 none) : see below;;
679 /*) ;;
680 *) # User-specified key pathnames are relative to the configuration
681 # directory
682 keyfile="${config_directory}/${keyfile}";;
683 esac
684 if [ "${keyfile}" = "none" ]; then keyfile= ; fi
685 }
686
687 check_key() {
688 read_key "$1" >/dev/null && return 0
689 $FATAL "no private key found in file: $1"
690 return 1
691 }
692
693 # Create new key or copy existing if specified.
694 #
695 ensure_key() {
696 _algo=$1; shift
697 _bits=$1; shift
698 stamp=`TZ=UTC date +%Y%m%d-%H%M%S`
699
700 case $_algo in
701 "") $FATAL "Internal error: empty algorithm "; return 1;;
702 $rsa) keyfile="${config_directory}/key-${stamp}.pem"
703 certfile="${config_directory}/cert-${stamp}.pem";;
704 $ecdsa) keyfile="${config_directory}/eckey-${stamp}.pem"
705 certfile="${config_directory}/eccert-${stamp}.pem";;
706 *) $FATAL "Internal error: bad algorithm '${_algo}'"
707 return 1;;
708 esac
709
710 if [ -r "${keyfile}" ]; then
711 $FATAL "New private key file already exists: ${keyfile}"
712 return 1
713 fi
714 if [ -r "${certfile}" ]; then
715 $FATAL "New certificate file already exists: ${certfile}"
716 return 1
717 fi
718
719 if [ -n "$1" ]; then
720 copy_key "${_algo}" "${_bits}" "$1" "${keyfile}" && return 0
721 else
722 create_key "${_algo}" "${_bits}" "${keyfile}" && return 0
723 fi
724 rm -f "${keyfile}"
725 return 1
726 }
727
728 init_random_source() {
729 tls_random_source=$1
730
731 if [ -z "${tls_random_source}" ]; then
732 tls_random_source=`$postconf -xh tls_random_source`
733 fi
734 if [ -n "${tls_random_source}" ]; then
735 return 0
736 fi
737 if [ -r /dev/urandom ]
738 then
739 tls_random_source=dev:/dev/urandom
740 else
741 $FATAL no default TLS random source defined and no /dev/urandom
742 return 1
743 fi
744 }
745
746 # Don't be too clever by half.
747 all_default() {
748 for var in "$@"
749 do
750 val=`$postconf -nh "${var}"`
751 if [ -n "$val" ]; then return 1; fi
752 done
753 return 0
754 }
755
756 # Select read-write database type for TLS session caches.
757 #
758 get_cache_db_type() {
759 var=$1; shift
760 prio=0
761 ret=1
762 for _dbtype in `$postconf -m`
763 do
764 _prio=0
765 case $_dbtype in
766 lmdb) _prio=2;;
767 btree) _prio=1;;
768 esac
769 if [ "$_prio" -gt "$prio" ]
770 then
771 eval "$var=\$_dbtype"
772 prio=$_prio
773 ret=0
774 fi
775 done
776 return $ret
777 }
778
779 deploy_server_cert() {
780 certfile=$1; shift
781 keyfile=$1; shift
782 deploy=$1; shift
783
784 # Sets key_algo, key_param and cert_param
785 check_key "$keyfile" || return 1
786
787 cd=`cert_pubkey_dgst "${certfile}"` || {
788 $FATAL "error computing certificate public key digest"
789 return 1
790 }
791 kd=`pubkey_dgst "$keyfile"` || {
792 $FATAL "error computing public key digest"
793 return 1
794 }
795
796 if [ "$cd" != "$kd" ]; then
797 $FATAL "Certificate in ${certfile} does not match key in ${keyfile}"
798 return 1
799 fi
800
801 set -- \
802 "${key_param} = ${keyfile}" \
803 "${cert_param} = ${certfile}"
804
805 if [ "${deploy}" = "enable" ]; then
806 set -- "$@" \
807 "smtpd_tls_security_level = may" \
808 "smtpd_tls_received_header = yes" \
809 "smtpd_tls_loglevel = 1"
810 fi
811
812 if [ -n "${tls_random_source}" ]; then
813 set -- "$@" "tls_random_source = ${tls_random_source}"
814 fi
815
816 # All in one shot, since postconf delays modifying "hot" main.cf files.
817 $postconf -e "$@" || return 1
818 }
819
820 # Prepare a new cert and perhaps re-use any existing private key.
821 #
822 new_server_cert() {
823 algo=$1; shift
824 bits=$1; shift
825 oldkey=$1; shift
826 deploy=$1; shift
827
828 # resets keyfile (copy or else new) and new certfile
829 ensure_key "$algo" "$bits" "${oldkey}" || return 1
830 create_cert "${keyfile}" "${certfile}" "$@" || return 1
831 if [ -n "${deploy}" ]; then
832 deploy_server_cert "${certfile}" "${keyfile}" "${deploy}" || return 1
833 fi
834
835 (
836 if [ -z "${deploy}" ]; then
837 info_created "${certfile}" "${keyfile}" "${deploy}"
838 else
839 info_server_deployed "${certfile}" "${keyfile}" "${deploy}"
840 fi
841 info_csr "${certfile}" "${keyfile}" "${deploy}"
842 info_tlsa "${certfile}" "${keyfile}" "${deploy}"
843 if [ -z "${oldkey}" ]; then
844 info_dane_dns "${certfile}" "${keyfile}" "${deploy}"
845 fi
846 ) | $INFO
847 }
848
849 enable_client() {
850 if all_default ${client_settings}
851 then
852 set -- \
853 "smtp_tls_security_level = may" \
854 "smtp_tls_loglevel = 1"
855
856 if get_cache_db_type dbtype
857 then
858 set -- "$@" \
859 "smtp_tls_session_cache_database = ${dbtype}:${data_directory}/smtp_scache"
860 fi
861
862 if [ -n "${tls_random_source}" ]; then
863 set -- "$@" "tls_random_source = ${tls_random_source}"
864 fi
865
866 # All in one shot, since postconf delays modifying "hot" main.cf files.
867 $postconf -e "$@" || return 1
868 info_client_deployed
869 else
870 info_enable_client
871 fi | $INFO
872 }
873
874 enable_server() {
875 algo=$1; shift
876 bits=$1; shift
877
878 if all_default ${server_settings}
879 then
880 # algo bits keyfile deploy [hostnames ...]
881 new_server_cert "${algo}" "${bits}" "" "enable" "$@" || return 1
882 else
883 info_enable_server | $INFO
884 fi
885 }
886
887 output_server_tlsa() {
888 hostname=$1
889 check_key "$2" || return 1
890 data=`pubkey_dgst "$2"` || return 1
891 if [ -z "$data" ]
892 then
893 $FATAL error computing SHA2-256 SPKI digest of "$key"
894 return 1
895 fi
896 echo "_25._tcp.$hostname. IN TLSA 3 1 1 $data"
897 }
898
899 #
900 # Parse JCL
901 #
902 case $1 in
903 enable-client)
904 cmd=$1; shift; OPTIND=1
905 rand=
906 while getopts :r: _opt
907 do
908 case $_opt in
909 r) rand="${OPTARG}";;
910 *) $FATAL "usage: postfix tls $cmd [-r devrandom]"
911 exit 1;;
912 esac
913 done
914
915 # No positional arguments supported with enable-client
916 if [ $# -ge "${OPTIND}" ]; then
917 $FATAL "usage: postfix tls $cmd [-r devrandom]"
918 exit 1
919 fi
920 # But, shift anyway
921 shift `expr $OPTIND - 1`
922
923 init_random_source "${rand}" || exit 1
924 enable_client || exit 1
925 ;;
926
927 enable-server)
928 cmd=$1; shift; OPTIND=1
929 algo=$DEFALG
930 bits=
931 rand=
932 while getopts :a:b:r: _opt
933 do
934 case $_opt in
935 a) algo="${OPTARG}";;
936 b) bits="${OPTARG}";;
937 r) rand="${OPTARG}";;
938 *) $FATAL "usage: postfix tls $cmd [-a algorithm] [-b bits ] [-r devrandom] [hostname ...]"
939 exit 1;;
940 esac
941 done
942
943 # Here positional arguments are hostnames for the new certificate, as
944 # many as the user wants
945 shift `expr $OPTIND - 1`
946
947 case $algo in
948 "") $FATAL "Internal error: empty algorithm "; return 1;;
949 $rsa) : ${bits:=${RSA_BITS}};;
950 $ecdsa) : ${bits:=${EC_CURVE}};;
951 *) $FATAL "Unsupported private key algorithm: $algo"
952 exit 1;;
953 esac
954
955 init_random_source "${rand}" || exit 1
956 enable_server "${algo}" "${bits}" "$@" || exit 1
957 ;;
958
959 new-server-key)
960 cmd=$1; shift; OPTIND=1
961 algo=$DEFALG
962 while getopts :a:b: _opt
963 do
964 case $_opt in
965 a) algo="${OPTARG}";;
966 b) bits="${OPTARG}";;
967 *) $FATAL "usage: postfix tls $cmd [-a algorithm] [-b bits ] [hostname ...]"
968 exit 1;;
969 esac
970 done
971
972 # Here positional arguments are hostnames for the new certificate, as
973 # many as the user wants
974 shift `expr $OPTIND - 1`
975
976 case $algo in
977 "") $FATAL "Internal error: empty algorithm "; return 1;;
978 $rsa) : ${bits:=${RSA_BITS}};;
979 $ecdsa) : ${bits:=${EC_CURVE}};;
980 *) $FATAL "Unsupported public key algorithm: $algo"
981 exit 1;;
982 esac
983
984 # Force new key
985 new_server_cert "${algo}" "${bits}" "" "" "$@" || exit 1
986 ;;
987
988 new-server-cert)
989 cmd=$1; shift; OPTIND=1
990 algo=$DEFALG
991 while getopts :a:b: _opt
992 do
993 case $_opt in
994 a) algo="${OPTARG}";;
995 b) bits="${OPTARG}";;
996 *) $FATAL "usage: postfix tls $cmd [-a algorithm] [-b bits ] [hostname ...]"
997 exit 1;;
998 esac
999 done
1000
1001 # Here positional arguments are hostnames for the new certificate, as
1002 # many as the user wants
1003 shift `expr $OPTIND - 1`
1004
1005 case $algo in
1006 "") $FATAL "Invalid empty key algorithm"; exit 1;;
1007 $rsa) : ${bits:=${RSA_BITS}};;
1008 $ecdsa) : ${bits:=${EC_CURVE}};;
1009 *) $FATAL "Unsupported private key algorithm: $algo"
1010 exit 1;;
1011 esac
1012
1013 # Existing keyfile or empty
1014 set_keyfile "${algo}"
1015
1016 if [ -n "${keyfile}" -a ! -f "${keyfile}" ]; then
1017 echo "Key file: ${keyfile} not found, creating new keys" | $WARN
1018 keyfile=
1019 fi
1020
1021 # Try to re-use (copy) existing key.
1022 new_server_cert "${algo}" "${bits}" "${keyfile}" "" "$@" || exit 1
1023 ;;
1024
1025 deploy-server-cert)
1026 if [ $# -ne 3 ]; then
1027 $FATAL "usage: postfix tls $1 certfile keyfile"
1028 exit 1
1029 fi
1030 shift
1031
1032 # User-specified key and cert pathnames are relative to the
1033 # configuration directory
1034 #
1035 case "${1}" in
1036 /*) certfile="${1}" ;;
1037 *) certfile="${config_directory}/${1}" ;;
1038 esac
1039 case "${2}" in
1040 /*) keyfile="${2}" ;;
1041 *) keyfile="${config_directory}/${2}" ;;
1042 esac
1043
1044 deploy_server_cert "${certfile}" "${keyfile}" || exit 1
1045 info_server_deployed "${certfile}" "${keyfile}" "deploy" | $INFO
1046 ;;
1047
1048 output-server-csr)
1049 cmd=$1; shift; OPTIND=1
1050 k=
1051 while getopts :k: _opt
1052 do
1053 case $_opt in
1054 k) k="${OPTARG}";;
1055 *) $FATAL "usage: postfix tls $cmd [-k keyfile] [hostname ...]"
1056 exit 1;;
1057 esac
1058 done
1059
1060 # Here positional arguments are hostnames for the new certificate, as
1061 # many as the user wants
1062 shift `expr $OPTIND - 1`
1063
1064 if [ -n "${k}" ]; then
1065 set_keyfile "${k}"
1066 else
1067 for _algo in $rsa $ecdsa
1068 do
1069 set_keyfile "${_algo}"
1070 if [ -n "${keyfile}" ]; then
1071 break
1072 fi
1073 done
1074 fi
1075
1076 if [ -z "${keyfile}" -o ! -r "${keyfile}" ]; then
1077 $FATAL "No usable keyfile specified or configured"
1078 exit 1
1079 fi
1080
1081 # Default <hostname> from $myhostname
1082 if [ $# -eq 0 ]; then
1083 set_fqdn
1084 set -- "$fqdn"
1085 fi
1086
1087 # Output a CSR for the requested names
1088 output_server_csr "$keyfile" "$@" || exit 1
1089 ;;
1090
1091 output-server-tlsa)
1092 cmd=$1; shift; OPTIND=1
1093 hostname=
1094 while getopts :h: _opt
1095 do
1096 case $_opt in
1097 h) hostname="${OPTARG}";;
1098 *) $FATAL "usage: postfix tls $cmd [-h hostname] [keyfile ...]"
1099 exit 1;;
1100 esac
1101 done
1102 set_fqdn "${hostname}"
1103
1104 # Here positional arguments are keyfiles for which we ouput "3 1 1"
1105 # TLSA RRs, as many keyfiles as the user wants. By default the live
1106 # RSA and/or ECDSA keys.
1107 shift `expr $OPTIND - 1`
1108
1109 if [ $# -eq 0 ]; then set -- $rsa $ecdsa; fi
1110
1111 found=
1112 for _k in "$@"
1113 do
1114 set_keyfile "${_k}"
1115 if [ -z "${keyfile}" ]; then continue; fi
1116 echo "; ${keyfile}"
1117 output_server_tlsa "${fqdn}" "${keyfile}" || exit 1
1118 found=1
1119 done
1120 if [ -z "${found}" ]; then
1121 $FATAL "No usable keyfiles specified or configured"
1122 exit 1
1123 fi
1124 ;;
1125
1126 all-default-client)
1127 cmd=$1; shift; OPTIND=1
1128
1129 # No arguments for all-default-client
1130 if [ $# -ge "${OPTIND}" ]; then
1131 $FATAL "usage: postfix tls $cmd"
1132 exit 1
1133 fi
1134
1135 all_default ${client_settings} || exit 1
1136 ;;
1137
1138 all-default-server)
1139 cmd=$1; shift; OPTIND=1
1140
1141 # No arguments for all-default-server
1142 if [ $# -ge "${OPTIND}" ]; then
1143 $FATAL "usage: postfix tls $cmd"
1144 exit 1
1145 fi
1146
1147 all_default ${server_settings} || exit 1
1148 ;;
1149
1150 *)
1151 $ERROR "unknown tls command: '$1'"
1152 $FATAL "usage: postfix tls enable-client (or enable-server, new-server-key, new-server-cert, deploy-server-cert, output-server-csr, output-server-tlsa, all-default-client, all-default-server)"
1153 exit 1
1154 ;;
1155
1156 esac
1157