Dual certificate support (eg ECDSA with RSA fallback) (#1801)

* feat: Change Postfix smtpd_tls key and cert files to chain_files

Since Postfix 3.4, `smtpd_tls_cert_file` and `smtpd_tls_key_file` have been deprecated in favor of `smtpd_tls_chain_files` which supports a list of values where a single or sequence of file paths provide a private key followed by it's certificate chain.

* feat: Dual certificate support

`smtpd_tls_chain_files` allows for multiple key+cert bundles so that you can provide different key types, such as ECDSA and RSA.

To maintain compatibility with the current CERT/KEY ENV vars only a 2nd certificate is supported.

Since Dovecot 2.2.31 a related feature is also available, but it is limited to only providing one alternative certificate via separate cert and key settings.

---

This feature enables support for multiple certificates, eg for serving modern ECDSA certs with RSA as fallback.

* chore: Refactor variable names to meet style guide

Improved some comments too.

* chore: Have function definitions respect style guide

* chore: Minor edits to comments

* chore: Expand on comments for maintenance, alert of insecure config

When `SSL_TYPE` isn't properly setup, we're still offering SSL connections but not warning in logs about the insecurity of such, or why a misconfiguration may have occurred.

This commit more clearly communicates to the user that they should look into the issue before considering deploying to production.

The `TODO` comments communicate to any future maintainer to consider treating these improper configs as disabling TLS instead.

* fix: Use `snakeoil` cert

I mistakenly thought this was placeholder text, which broke some tests. This adds the two files in the correct order (private key followed by cert/chain), to fix that issue.

* fix: Disable alt cert for Dovecot if necessary

Certain scenarios may persist state of previously configured alt cert via ENV vars that are removed from a future run. If the config is not reset to original immutable state, this will correctly disable the config from using alt cert unintentionally.

* fix: Satisfy ShellCheck lint

By switching from string var to array / list expansion, this better stores the extracted result and applies it in a manner that ShellCheck linting approves, removing the need to disable the rule.

* feat: Support dual cert test

Few tweaks to the test script allows re-purposing it for covering dual cert support as well.

* chore: Rearranged cert and key lines

A little reorganization, mostly placing private key ahead of related cert lines.

* chore: Refactor `_set_certificate`

This should make the parameters a little less confusing.

Previously was 3 parameters, but the Postfix parameter (1st) may look like two variables if you don't pay attention to the surrounding quotes; while the Dovecot parameters (2nd + 3rd) would have an opposing order. There was also a variant where the `FULLKEYCHAIN` var was passed in three times.

Now it's two params, with the 2nd param as an optional one. If the 2nd param is provided, then the two params are in the order of private key then certificate, otherwise if only a single parameter it's a single PEM file with the full cert chain and private key bundled.

This avoids implying that Postfix and Dovecot might use different files.

* chore: Document current state of `SSL_TYPE` logic better

Inlined for the benefit of anyone else maintaining this section if I'm unable to address the concerns within my own time.

* docs: ENV vars

`TLS_LEVEL=old` isn't in the codebase anymore, not likely to be relevant to retain.

No point in documenting what is considered invalid / unsupported config value in the first place for `SSL_TYPE`.

`SSL_TYPE=manual` was missing documentation for both related file path ENV vars, they've been added along with their alt fallback variants.

* chore: Update Dovecot LMTP SSL test config

Not sure how relevant this is, the file isn't complete sync with the main dovecot `10-ssl.conf` config, adding the support just in case.

* chore: Rename `FULLKEYCHAIN` to avoid confusion

There doesn't appear to be a standardized name for this type of file bundle, and `keychain` may be misleading (fullkeychain often provides macOS keychain  results on search engines).

Opting for a more explicit `KEY_WITH_FULLCHAIN` name instead.

* fix: Invalid var name

`_set_certificate` refactor commit accidentally changed a var name and committed that breaking the dual cert support (thanks tests!).

* test: Refactor `mail_ssl_manual.bats`

Proper test return values instead of `wc -l` based checking.

Tests with dual cert support active, tests that feature (to better detect failure case.

Third test case was unable to verify new self-signed certificate, added new certs signed with self-signed root CA.

Adjusted openssl `CApath` parameter to use `CAfile` instead as `letsencrypt` cert was replaced thus CA cert is missing from the system trust store.

* test: Properly check for files in `mail_ssl_manual.bats`

Fixes lint error.

Also realized I was accidentally asserting a file exists in the test environment, not within the container.

Resolved that and also added an additional test case to ensure the ENV var files are valid when passed in, in the event a change misconfigures them and that the issue is identified earlier.

* chore: Apply PR review feedback

Better format some strings that had mixed quotes when they weren't necessary.

Additionally DRYed up the config path for Postfix and Dovecot within the `_setup_ssl` method.

Co-authored-by: Georg Lauterbach <infrastructure@itbsd.com>
This commit is contained in:
Brennan Kinney 2021-02-22 11:43:41 +13:00 committed by GitHub
parent a7ecb0ea8b
commit d02ebc922c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 624 additions and 113 deletions

View file

@ -63,12 +63,13 @@ Otherwise, `iptables` won't be able to ban IPs.
##### SSL_TYPE
- **empty** => SSL disabled
- letsencrypt => Enables Let's Encrypt certificates
- custom => Enables custom certificates
- **empty** => SSL disabled.
- letsencrypt => Enables Let's Encrypt certificates.
- custom => Enables custom certificates.
- manual => Let you manually specify locations of your SSL certificates for non-standard cases
- self-signed => Enables self-signed certificates
- _any other value_ => SSL required, settings by default
- Requires: `SSL_CERT_PATH` and `SSL_KEY_PATH` ENV vars to be set to the location of the files within the container.
- Optional: `SSL_ALT_CERT_PATH` and `SSL_ALT_KEY_PATH` allow providing a 2nd certificate as a fallback for dual (aka hybrid) certificate support. Useful for ECDSA with an RSA fallback. Presently only `manual` mode supports this feature.
- self-signed => Enables self-signed certificates.
Please read [the SSL page in the wiki](https://github.com/docker-mailserver/docker-mailserver/wiki/Configure-SSL) for more information.
@ -77,7 +78,6 @@ Please read [the SSL page in the wiki](https://github.com/docker-mailserver/dock
- **empty** => modern
- modern => Enables TLSv1.2 and modern ciphers only. (default)
- intermediate => Enables TLSv1, TLSv1.1 and TLSv1.2 and broad compatibility ciphers.
- old => NOT implemented. If you really need it, then customize the TLS ciphers overriding postfix and dovecot settings [wiki](https://github.com/docker-mailserver/docker-mailserver/wiki/)
##### SPOOF_PROTECTION

View file

@ -43,8 +43,6 @@ NETWORK_INTERFACE=
# empty => modern
# modern => Enables TLSv1.2 and modern ciphers only. (default)
# intermediate => Enables TLSv1, TLSv1.1 and TLSv1.2 and broad compatibility ciphers.
# old => NOT implemented. If you really need it, then customize the TLS ciphers overriding postfix and dovecot settings
# (https://github.com/docker-mailserver/docker-mailserver/wiki/)
TLS_LEVEL=
# Configures the handling of creating mails with forged sender addresses.
@ -94,6 +92,15 @@ SMTP_ONLY=
# self-signed => Enables self-signed certificates
SSL_TYPE=
# These are only supported with `SSL_TYPE=manual`.
# Provide the path to your cert and key files that you've mounted access to within the container.
SSL_CERT_PATH=
SSL_KEY_PATH=
# Optional: A 2nd certificate can be supported as fallback (dual cert support), eg ECDSA with an RSA fallback.
# Useful for additional compatibility with older MTA and MUA (eg pre-2015).
SSL_ALT_CERT_PATH=
SSL_ALT_KEY_PATH=
# Set how many days a virusmail will stay on the server before being deleted
# empty => 7 days
VIRUSMAILS_DELETE_DELAY=

View file

@ -11,6 +11,8 @@
# certificate, just make sure to update the domains in dovecot-openssl.cnf
ssl_cert = </etc/dovecot/ssl/dovecot.pem
ssl_key = </etc/dovecot/ssl/dovecot.key
#ssl_alt_cert = </path/to/alternative/cert.pem
#ssl_alt_key = </path/to/alternative/key.pem
# If key file is password protected, give the password here. Alternatively
# give it when starting dovecot with -p parameter. Since this file is often

View file

@ -18,10 +18,11 @@ inet_interfaces = all
inet_protocols = all
# TLS parameters
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
#smtpd_tls_CAfile=
#smtp_tls_CAfile=
# These [snakeoil files actually exist](https://askubuntu.com/questions/396120/what-is-the-purpose-of-the-ssl-cert-snakeoil-key), but shouldn't ever be used in production!
# If no `SSL_TYPE` env is set, "plaintext" is configured, but still accepts SSL with these:
smtpd_tls_chain_files = /etc/ssl/private/ssl-cert-snakeoil.key /etc/ssl/certs/ssl-cert-snakeoil.pem
#smtpd_tls_CAfile =
#smtp_tls_CAfile =
smtpd_tls_security_level = may
smtpd_tls_loglevel = 1
smtp_tls_security_level = may

View file

@ -1135,21 +1135,80 @@ function _setup_ssl
{
_notify 'task' 'Setting up SSL'
function _apply_tls_level()
local POSTFIX_CONFIG_MAIN='/etc/postfix/main.cf'
local DOVECOT_CONFIG_SSL='/etc/dovecot/conf.d/10-ssl.conf'
# Primary certificate to serve for TLS
# NOTE: The `sed` substituion delimiter uses `~` instead of `/` due to file paths as values
function _set_certificate
{
local POSTFIX_KEY_WITH_FULLCHAIN=$1
local DOVECOT_KEY=$1
local DOVECOT_CERT=$1
# If 2nd param is provided, we've been provided separate key and cert instead of a fullkeychain
if [[ -n $2 ]]
then
local PRIVATE_KEY=$1
local CERT_CHAIN=$2
POSTFIX_KEY_WITH_FULLCHAIN="${PRIVATE_KEY} ${CERT_CHAIN}"
DOVECOT_KEY="${PRIVATE_KEY}"
DOVECOT_CERT="${CERT_CHAIN}"
fi
# Postfix configuration
# NOTE: `smtpd_tls_chain_files` expects private key defined before public cert chain
# May be a single PEM file or a sequence of files, so long as the order is key->leaf->chain
sed -i "s~^smtpd_tls_chain_files =.*~smtpd_tls_chain_files = ${POSTFIX_KEY_WITH_FULLCHAIN}~" "${POSTFIX_CONFIG_MAIN}"
# Dovecot configuration
sed -i "s~^ssl_key = <.*~ssl_key = <${DOVECOT_KEY}~" "${DOVECOT_CONFIG_SSL}"
sed -i "s~^ssl_cert = <.*~ssl_cert = <${DOVECOT_CERT}~" "${DOVECOT_CONFIG_SSL}"
}
# Enables supporting two certificate types such as ECDSA with an RSA fallback
function _set_alt_certificate
{
local COPY_KEY_FROM_PATH=$1
local COPY_CERT_FROM_PATH=$2
local PRIVATE_KEY_ALT='/etc/postfix/ssl/fallback_key'
local CERT_CHAIN_ALT='/etc/postfix/ssl/fallback_cert'
cp "${COPY_KEY_FROM_PATH}" "${PRIVATE_KEY_ALT}"
cp "${COPY_CERT_FROM_PATH}" "${CERT_CHAIN_ALT}"
chmod 600 "${PRIVATE_KEY_ALT}"
chmod 600 "${CERT_CHAIN_ALT}"
# Postfix configuration
# NOTE: This operation doesn't replace the line, it appends to the end of the line.
# Thus this method should only be used when this line has explicitly been replaced earlier in the script.
# Otherwise without `docker-compose down` first, a `docker-compose up` may
# persist previous container state and cause a failure in postfix configuration.
sed -i "s~^smtpd_tls_chain_files =.*~& ${PRIVATE_KEY_ALT} ${CERT_CHAIN_ALT}~" "${POSTFIX_CONFIG_MAIN}"
# Dovecot configuration
# Conditionally checks for `#`, in the event that internal container state is accidentally persisted,
# can be caused by: `docker-compose up` run again after a `ctrl+c`, without running `docker-compose down`
sed -i "s~^#\?ssl_alt_key = <.*~ssl_alt_key = <${PRIVATE_KEY_ALT}~" "${DOVECOT_CONFIG_SSL}"
sed -i "s~^#\?ssl_alt_cert = <.*~ssl_alt_cert = <${CERT_CHAIN_ALT}~" "${DOVECOT_CONFIG_SSL}"
}
function _apply_tls_level
{
local TLS_CIPHERS_ALLOW=$1
local TLS_PROTOCOL_IGNORE=$2
local TLS_PROTOCOL_MINIMUM=$3
# Postfix configuration
sed -i 's/^smtpd_tls_mandatory_protocols =.*/smtpd_tls_mandatory_protocols = '"${TLS_PROTOCOL_IGNORE}/" /etc/postfix/main.cf
sed -i 's/^smtpd_tls_protocols =.*/smtpd_tls_protocols = '"${TLS_PROTOCOL_IGNORE}/" /etc/postfix/main.cf
sed -i 's/^smtp_tls_protocols =.*/smtp_tls_protocols = '"${TLS_PROTOCOL_IGNORE}/" /etc/postfix/main.cf
sed -i 's/^tls_high_cipherlist =.*/tls_high_cipherlist = '"${TLS_CIPHERS_ALLOW}/" /etc/postfix/main.cf
sed -i "s/^smtpd_tls_mandatory_protocols =.*/smtpd_tls_mandatory_protocols = ${TLS_PROTOCOL_IGNORE}/" "${POSTFIX_CONFIG_MAIN}"
sed -i "s/^smtpd_tls_protocols =.*/smtpd_tls_protocols = ${TLS_PROTOCOL_IGNORE}/" "${POSTFIX_CONFIG_MAIN}"
sed -i "s/^smtp_tls_protocols =.*/smtp_tls_protocols = ${TLS_PROTOCOL_IGNORE}/" "${POSTFIX_CONFIG_MAIN}"
sed -i "s/^tls_high_cipherlist =.*/tls_high_cipherlist = ${TLS_CIPHERS_ALLOW}/" "${POSTFIX_CONFIG_MAIN}"
# Dovecot configuration (secure by default though)
sed -i 's/^ssl_min_protocol =.*/ssl_min_protocol = '"${TLS_PROTOCOL_MINIMUM}/" /etc/dovecot/conf.d/10-ssl.conf
sed -i 's/^ssl_cipher_list =.*/ssl_cipher_list = '"${TLS_CIPHERS_ALLOW}/" /etc/dovecot/conf.d/10-ssl.conf
sed -i "s/^ssl_min_protocol =.*/ssl_min_protocol = ${TLS_PROTOCOL_MINIMUM}/" "${DOVECOT_CONFIG_SSL}"
sed -i "s/^ssl_cipher_list =.*/ssl_cipher_list = ${TLS_CIPHERS_ALLOW}/" "${DOVECOT_CONFIG_SSL}"
}
# TLS strength/level configuration
@ -1181,6 +1240,8 @@ function _setup_ssl
esac
# SSL certificate Configuration
# TODO: Refactor this feature, it's been extended multiple times for specific inputs/providers unnecessarily.
# NOTE: Some `SSL_TYPE` logic uses mounted certs/keys directly, some make an internal copy either retaining filename or renaming, chmod inconsistent.
case "${SSL_TYPE}" in
"letsencrypt" )
_notify 'inf' "Configuring SSL using 'letsencrypt'"
@ -1188,6 +1249,9 @@ function _setup_ssl
local LETSENCRYPT_DOMAIN=""
local LETSENCRYPT_KEY=""
# 2020 feature intended for Traefik v2 support only:
# https://github.com/docker-mailserver/docker-mailserver/pull/1553
# Uses `key.pem` and `fullchain.pem`
if [[ -f /etc/letsencrypt/acme.json ]]
then
if ! _extract_certs_from_acme "${SSL_DOMAIN}"
@ -1231,13 +1295,14 @@ function _setup_ssl
then
_notify 'inf' "Adding ${LETSENCRYPT_DOMAIN} SSL certificate to the postfix and dovecot configuration"
# Postfix configuration
sed -i -r 's~smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem~smtpd_tls_cert_file=/etc/letsencrypt/live/'"${LETSENCRYPT_DOMAIN}"'/fullchain.pem~g' /etc/postfix/main.cf
sed -i -r 's~smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key~smtpd_tls_key_file=/etc/letsencrypt/live/'"${LETSENCRYPT_DOMAIN}"'/'"${LETSENCRYPT_KEY}"'\.pem~g' /etc/postfix/main.cf
# LetsEncrypt `fullchain.pem` and `privkey.pem` contents are detailed here from CertBot:
# https://certbot.eff.org/docs/using.html#where-are-my-certificates
# `key.pem` was added for `simp_le` support (2016): https://github.com/docker-mailserver/docker-mailserver/pull/288
# `key.pem` is also a filename used by the `_extract_certs_from_acme` method (implemented for Traefik v2 only)
local PRIVATE_KEY="/etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/${LETSENCRYPT_KEY}.pem"
local CERT_CHAIN="/etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/fullchain.pem"
# Dovecot configuration
sed -i -e 's~ssl_cert = </etc/dovecot/ssl/dovecot\.pem~ssl_cert = </etc/letsencrypt/live/'"${LETSENCRYPT_DOMAIN}"'/fullchain\.pem~g' /etc/dovecot/conf.d/10-ssl.conf
sed -i -e 's~ssl_key = </etc/dovecot/ssl/dovecot\.key~ssl_key = </etc/letsencrypt/live/'"${LETSENCRYPT_DOMAIN}"'/'"${LETSENCRYPT_KEY}"'\.pem~g' /etc/dovecot/conf.d/10-ssl.conf
_set_certificate "${PRIVATE_KEY}" "${CERT_CHAIN}"
_notify 'inf' "SSL configured with 'letsencrypt' certificates"
fi
@ -1252,85 +1317,93 @@ function _setup_ssl
mkdir -p /etc/postfix/ssl
cp "/tmp/docker-mailserver/ssl/${HOSTNAME}-full.pem" /etc/postfix/ssl
# Postfix configuration
sed -i -r 's~smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem~smtpd_tls_cert_file=/etc/postfix/ssl/'"${HOSTNAME}"'-full.pem~g' /etc/postfix/main.cf
sed -i -r 's~smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key~smtpd_tls_key_file=/etc/postfix/ssl/'"${HOSTNAME}"'-full.pem~g' /etc/postfix/main.cf
# Private key with full certificate chain all in a single PEM file
# NOTE: Dovecot works fine still as both values are bundled into the keychain
local KEY_WITH_FULLCHAIN='/etc/postfix/ssl/'"${HOSTNAME}"'-full.pem'
# Dovecot configuration
sed -i -e 's~ssl_cert = </etc/dovecot/ssl/dovecot\.pem~ssl_cert = </etc/postfix/ssl/'"${HOSTNAME}"'-full\.pem~g' /etc/dovecot/conf.d/10-ssl.conf
sed -i -e 's~ssl_key = </etc/dovecot/ssl/dovecot\.key~ssl_key = </etc/postfix/ssl/'"${HOSTNAME}"'-full\.pem~g' /etc/dovecot/conf.d/10-ssl.conf
_set_certificate "${KEY_WITH_FULLCHAIN}"
_notify 'inf' "SSL configured with 'CA signed/custom' certificates"
fi
;;
"manual" )
# Lets you manually specify the location of the SSL Certs to use. This gives you some more control over this whole processes (like using kube-lego to generate certs)
# Lets you manually specify the location of the SSL Certs to use. This gives you some more control over this whole processes (like using kube-lego to generate certs)
if [[ -n ${SSL_CERT_PATH} ]] && [[ -n ${SSL_KEY_PATH} ]]
then
_notify 'inf' "Configuring certificates using cert ${SSL_CERT_PATH} and key ${SSL_KEY_PATH}"
mkdir -p /etc/postfix/ssl
cp "${SSL_CERT_PATH}" /etc/postfix/ssl/cert
cp "${SSL_KEY_PATH}" /etc/postfix/ssl/key
chmod 600 /etc/postfix/ssl/cert
cp "${SSL_CERT_PATH}" /etc/postfix/ssl/cert
chmod 600 /etc/postfix/ssl/key
chmod 600 /etc/postfix/ssl/cert
# Postfix configuration
sed -i -r 's~smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem~smtpd_tls_cert_file=/etc/postfix/ssl/cert~g' /etc/postfix/main.cf
sed -i -r 's~smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key~smtpd_tls_key_file=/etc/postfix/ssl/key~g' /etc/postfix/main.cf
local PRIVATE_KEY='/etc/postfix/ssl/key'
local CERT_CHAIN='/etc/postfix/ssl/cert'
# Dovecot configuration
sed -i -e 's~ssl_cert = </etc/dovecot/ssl/dovecot\.pem~ssl_cert = </etc/postfix/ssl/cert~g' /etc/dovecot/conf.d/10-ssl.conf
sed -i -e 's~ssl_key = </etc/dovecot/ssl/dovecot\.key~ssl_key = </etc/postfix/ssl/key~g' /etc/dovecot/conf.d/10-ssl.conf
_set_certificate "${PRIVATE_KEY}" "${CERT_CHAIN}"
# Support for a fallback certificate, useful for hybrid/dual ECDSA + RSA certs
if [[ -n ${SSL_ALT_KEY_PATH} ]] && [[ -n ${SSL_ALT_CERT_PATH} ]]
then
_notify 'inf' "Configuring alternative certificates using cert ${SSL_ALT_CERT_PATH} and key ${SSL_ALT_KEY_PATH}"
_set_alt_certificate "${SSL_ALT_KEY_PATH}" "${SSL_ALT_CERT_PATH}"
else
# If the Dovecot settings for alt cert has been enabled (doesn't start with `#`),
# but required ENV var is missing, reset to disabled state:
sed -i 's~^ssl_alt_key = <.*~#ssl_alt_key = </path/to/alternative/key.pem~' "${DOVECOT_CONFIG_SSL}"
sed -i 's~^ssl_alt_cert = <.*~#ssl_alt_cert = </path/to/alternative/cert.pem~' "${DOVECOT_CONFIG_SSL}"
fi
_notify 'inf' "SSL configured with 'Manual' certificates"
fi
;;
"self-signed" )
# Adding self-signed SSL certificate if provided in 'postfix/ssl' folder
if [[ -e /tmp/docker-mailserver/ssl/${HOSTNAME}-cert.pem ]] \
&& [[ -e /tmp/docker-mailserver/ssl/${HOSTNAME}-key.pem ]] \
&& [[ -e /tmp/docker-mailserver/ssl/${HOSTNAME}-combined.pem ]] \
if [[ -e /tmp/docker-mailserver/ssl/${HOSTNAME}-key.pem ]] \
&& [[ -e /tmp/docker-mailserver/ssl/${HOSTNAME}-cert.pem ]] \
&& [[ -e /tmp/docker-mailserver/ssl/demoCA/cacert.pem ]]
then
_notify 'inf' "Adding ${HOSTNAME} SSL certificate"
mkdir -p /etc/postfix/ssl
cp "/tmp/docker-mailserver/ssl/${HOSTNAME}-cert.pem" /etc/postfix/ssl
cp "/tmp/docker-mailserver/ssl/${HOSTNAME}-key.pem" /etc/postfix/ssl
# Force permission on key file
cp "/tmp/docker-mailserver/ssl/${HOSTNAME}-cert.pem" /etc/postfix/ssl
chmod 600 "/etc/postfix/ssl/${HOSTNAME}-key.pem"
cp "/tmp/docker-mailserver/ssl/${HOSTNAME}-combined.pem" /etc/postfix/ssl
local PRIVATE_KEY="/etc/postfix/ssl/${HOSTNAME}-key.pem"
local CERT_CHAIN="/etc/postfix/ssl/${HOSTNAME}-cert.pem"
_set_certificate "${PRIVATE_KEY}" "${CERT_CHAIN}"
cp /tmp/docker-mailserver/ssl/demoCA/cacert.pem /etc/postfix/ssl
# Postfix configuration
sed -i -r 's~smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem~smtpd_tls_cert_file=/etc/postfix/ssl/'"${HOSTNAME}"'-cert.pem~g' /etc/postfix/main.cf
sed -i -r 's~smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key~smtpd_tls_key_file=/etc/postfix/ssl/'"${HOSTNAME}"'-key.pem~g' /etc/postfix/main.cf
sed -i -r 's~#smtpd_tls_CAfile=~smtpd_tls_CAfile=/etc/postfix/ssl/cacert.pem~g' /etc/postfix/main.cf
sed -i -r 's~#smtp_tls_CAfile=~smtp_tls_CAfile=/etc/postfix/ssl/cacert.pem~g' /etc/postfix/main.cf
ln -s /etc/postfix/ssl/cacert.pem "/etc/ssl/certs/cacert-${HOSTNAME}.pem"
# Dovecot configuration
sed -i -e 's~ssl_cert = </etc/dovecot/ssl/dovecot\.pem~ssl_cert = </etc/postfix/ssl/'"${HOSTNAME}"'-combined\.pem~g' /etc/dovecot/conf.d/10-ssl.conf
sed -i -e 's~ssl_key = </etc/dovecot/ssl/dovecot\.key~ssl_key = </etc/postfix/ssl/'"${HOSTNAME}"'-key\.pem~g' /etc/dovecot/conf.d/10-ssl.conf
# Have Postfix trust the self-signed CA (which is not installed within the OS trust store)
sed -i -r 's~^#?smtpd_tls_CAfile =.*~smtpd_tls_CAfile = /etc/postfix/ssl/cacert.pem~' "${POSTFIX_CONFIG_MAIN}"
sed -i -r 's~^#?smtp_tls_CAfile =.*~smtp_tls_CAfile = /etc/postfix/ssl/cacert.pem~' "${POSTFIX_CONFIG_MAIN}"
# Part of the original `self-signed` support, unclear why this symlink was required?
# May have been to support the now removed `Courier` (Dovecot replaced it):
# https://github.com/docker-mailserver/docker-mailserver/commit/1fb3aeede8ac9707cc9ea11d603e3a7b33b5f8d5
local PRIVATE_CA="/etc/ssl/certs/cacert-${HOSTNAME}.pem"
ln -s /etc/postfix/ssl/cacert.pem "${PRIVATE_CA}"
_notify 'inf' "SSL configured with 'self-signed' certificates"
fi
;;
'' )
# no SSL certificate, plain text access
# TODO: Postfix configuration still responds to TLS negotiations using snakeoil cert from default config
# TODO: Dovecot `ssl = yes` also allows TLS, both cases this is insecure and should probably instead enforce no TLS?
# Dovecot configuration
sed -i -e 's~#disable_plaintext_auth = yes~disable_plaintext_auth = no~g' /etc/dovecot/conf.d/10-auth.conf
sed -i -e 's~ssl = required~ssl = yes~g' /etc/dovecot/conf.d/10-ssl.conf
sed -i -e 's~ssl = required~ssl = yes~g' "${DOVECOT_CONFIG_SSL}"
_notify 'inf' "SSL configured with plain text access"
_notify 'warn' "(INSECURE!) SSL configured with plain text access. DO NOT USE FOR PRODUCTION DEPLOYMENT."
;;
* )
# Unknown option, default behavior, no action is required
_notify 'warn' "SSL configured by default"
_notify 'warn' "(INSECURE!) ENV var 'SSL_TYPE' is invalid. DO NOT USE FOR PRODUCTION DEPLOYMENT."
;;
esac
}

View file

@ -11,6 +11,8 @@ ssl = required
# certificate, just make sure to update the domains in dovecot-openssl.cnf
ssl_cert = </etc/dovecot/ssl/dovecot.pem
ssl_key = </etc/dovecot/ssl/dovecot.key
#ssl_alt_cert = </path/to/alternative/cert.pem
#ssl_alt_key = </path/to/alternative/key.pem
# If key file is password protected, give the password here. Alternatively
# give it when starting dovecot with -p parameter. Since this file is often

View file

@ -58,10 +58,7 @@ function teardown_file() {
@test "checking ssl: letsencrypt configuration is correct" {
#test domain has certificate files
run docker exec mail_lets_domain /bin/sh -c 'postconf | grep "smtpd_tls_cert_file = /etc/letsencrypt/live/my-domain.com/fullchain.pem" | wc -l'
assert_success
assert_output 1
run docker exec mail_lets_domain /bin/sh -c 'postconf | grep "smtpd_tls_key_file = /etc/letsencrypt/live/my-domain.com/key.pem" | wc -l'
run docker exec mail_lets_domain /bin/sh -c 'postconf | grep "smtpd_tls_chain_files = /etc/letsencrypt/live/my-domain.com/key.pem /etc/letsencrypt/live/my-domain.com/fullchain.pem" | wc -l'
assert_success
assert_output 1
run docker exec mail_lets_domain /bin/sh -c 'doveconf | grep "ssl_cert = </etc/letsencrypt/live/my-domain.com/fullchain.pem" | wc -l'
@ -71,10 +68,7 @@ function teardown_file() {
assert_success
assert_output 1
#test hostname has certificate files
run docker exec mail_lets_hostname /bin/sh -c 'postconf | grep "smtpd_tls_cert_file = /etc/letsencrypt/live/mail.my-domain.com/fullchain.pem" | wc -l'
assert_success
assert_output 1
run docker exec mail_lets_hostname /bin/sh -c 'postconf | grep "smtpd_tls_key_file = /etc/letsencrypt/live/mail.my-domain.com/privkey.pem" | wc -l'
run docker exec mail_lets_hostname /bin/sh -c 'postconf | grep "smtpd_tls_chain_files = /etc/letsencrypt/live/mail.my-domain.com/privkey.pem /etc/letsencrypt/live/mail.my-domain.com/fullchain.pem" | wc -l'
assert_success
assert_output 1
run docker exec mail_lets_hostname /bin/sh -c 'doveconf | grep "ssl_cert = </etc/letsencrypt/live/mail.my-domain.com/fullchain.pem" | wc -l'

View file

@ -1,3 +1,4 @@
#!/usr/bin/env bats
load 'test_helper/common'
function setup() {
@ -9,16 +10,34 @@ function teardown() {
}
function setup_file() {
# Internal copies made by `start-mailserver.sh`:
export PRIMARY_KEY='/etc/postfix/ssl/key'
export PRIMARY_CERT='/etc/postfix/ssl/cert'
export FALLBACK_KEY='/etc/postfix/ssl/fallback_key'
export FALLBACK_CERT='/etc/postfix/ssl/fallback_cert'
# Volume mounted certs:
export SSL_KEY_PATH='/config/ssl/key.ecdsa.pem'
export SSL_CERT_PATH='/config/ssl/cert.ecdsa.pem'
export SSL_ALT_KEY_PATH='/config/ssl/key.rsa.pem'
export SSL_ALT_CERT_PATH='/config/ssl/cert.rsa.pem'
local DOMAIN='example.test'
local PRIVATE_CONFIG
PRIVATE_CONFIG="$(duplicate_config_for_container .)"
docker run -d --name mail_manual_ssl \
-v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \
-v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \
-e SSL_TYPE=manual \
-e SSL_CERT_PATH=/tmp/docker-mailserver/letsencrypt/mail.my-domain.com/fullchain.pem \
-e SSL_KEY_PATH=/tmp/docker-mailserver/letsencrypt/mail.my-domain.com/privkey.pem \
-e DMS_DEBUG=0 \
-h mail.my-domain.com -t "${NAME}"
--volume "${PRIVATE_CONFIG}/:/tmp/docker-mailserver/" \
--volume "$(pwd)/test/test-files/ssl/${DOMAIN}/with_ca/ecdsa/:/config/ssl/:ro" \
--env DMS_DEBUG=0 \
--env SSL_TYPE='manual' \
--env SSL_KEY_PATH="${SSL_KEY_PATH}" \
--env SSL_CERT_PATH="${SSL_CERT_PATH}" \
--env SSL_ALT_KEY_PATH="${SSL_ALT_KEY_PATH}" \
--env SSL_ALT_CERT_PATH="${SSL_ALT_CERT_PATH}" \
--hostname "mail.${DOMAIN}" \
--tty \
"${NAME}" # Image name
wait_for_finished_setup_in_container mail_manual_ssl
}
@ -27,37 +46,69 @@ function teardown_file() {
}
@test "first" {
skip 'this test must come first to reliably identify when to run setup_file'
skip 'this test must come first to reliably identify when to run setup_file'
}
@test "checking ssl: ENV vars provided are valid files" {
assert docker exec mail_manual_ssl [ -f "${SSL_CERT_PATH}" ]
assert docker exec mail_manual_ssl [ -f "${SSL_KEY_PATH}" ]
assert docker exec mail_manual_ssl [ -f "${SSL_ALT_CERT_PATH}" ]
assert docker exec mail_manual_ssl [ -f "${SSL_ALT_KEY_PATH}" ]
}
@test "checking ssl: manual configuration is correct" {
run docker exec mail_manual_ssl /bin/sh -c 'grep -ir "/etc/postfix/ssl/cert" /etc/postfix/main.cf | wc -l'
assert_success
assert_output 1
run docker exec mail_manual_ssl /bin/sh -c 'grep -ir "/etc/postfix/ssl/cert" /etc/dovecot/conf.d/10-ssl.conf | wc -l'
assert_success
assert_output 1
run docker exec mail_manual_ssl /bin/sh -c 'grep -ir "/etc/postfix/ssl/key" /etc/postfix/main.cf | wc -l'
assert_success
assert_output 1
run docker exec mail_manual_ssl /bin/sh -c 'grep -ir "/etc/postfix/ssl/key" /etc/dovecot/conf.d/10-ssl.conf | wc -l'
assert_success
assert_output 1
local DOVECOT_CONFIG_SSL='/etc/dovecot/conf.d/10-ssl.conf'
run docker exec mail_manual_ssl grep '^smtpd_tls_chain_files =' '/etc/postfix/main.cf'
assert_success
assert_output "smtpd_tls_chain_files = ${PRIMARY_KEY} ${PRIMARY_CERT} ${FALLBACK_KEY} ${FALLBACK_CERT}"
run docker exec mail_manual_ssl grep '^ssl_key =' "${DOVECOT_CONFIG_SSL}"
assert_success
assert_output "ssl_key = <${PRIMARY_KEY}"
run docker exec mail_manual_ssl grep '^ssl_cert =' "${DOVECOT_CONFIG_SSL}"
assert_success
assert_output "ssl_cert = <${PRIMARY_CERT}"
run docker exec mail_manual_ssl grep '^ssl_alt_key =' "${DOVECOT_CONFIG_SSL}"
assert_success
assert_output "ssl_alt_key = <${FALLBACK_KEY}"
run docker exec mail_manual_ssl grep '^ssl_alt_cert =' "${DOVECOT_CONFIG_SSL}"
assert_success
assert_output "ssl_alt_cert = <${FALLBACK_CERT}"
}
@test "checking ssl: manual configuration copied files correctly " {
run docker exec mail_manual_ssl /bin/sh -c 'cmp -s /etc/postfix/ssl/cert /tmp/docker-mailserver/letsencrypt/mail.my-domain.com/fullchain.pem'
assert_success
run docker exec mail_manual_ssl /bin/sh -c 'cmp -s /etc/postfix/ssl/key /tmp/docker-mailserver/letsencrypt/mail.my-domain.com/privkey.pem'
assert_success
run docker exec mail_manual_ssl cmp -s "${PRIMARY_KEY}" "${SSL_KEY_PATH}"
assert_success
run docker exec mail_manual_ssl cmp -s "${PRIMARY_CERT}" "${SSL_CERT_PATH}"
assert_success
# Fallback cert
run docker exec mail_manual_ssl cmp -s "${FALLBACK_KEY}" "${SSL_ALT_KEY_PATH}"
assert_success
run docker exec mail_manual_ssl cmp -s "${FALLBACK_CERT}" "${SSL_ALT_CERT_PATH}"
assert_success
}
@test "checking ssl: manual cert works correctly" {
wait_for_tcp_port_in_container 587 mail_manual_ssl
run docker exec mail_manual_ssl /bin/sh -c "timeout 1 openssl s_client -connect 0.0.0.0:587 -starttls smtp -CApath /etc/ssl/certs/ | grep 'Verify return code: 10 (certificate has expired)'"
assert_success
wait_for_tcp_port_in_container 587 mail_manual_ssl
local TEST_COMMAND=(timeout 1 openssl s_client -connect mail.example.test:587 -starttls smtp)
local RESULT
# Should fail as a chain of trust is required to verify successfully:
RESULT=$(docker exec mail_manual_ssl "${TEST_COMMAND[@]}" | grep 'Verification error:')
assert_equal "${RESULT}" 'Verification error: unable to verify the first certificate'
# Provide the Root CA cert for successful verification:
local CA_CERT='/config/ssl/ca-cert.ecdsa.pem'
assert docker exec mail_manual_ssl [ -f "${CA_CERT}" ]
RESULT=$(docker exec mail_manual_ssl "${TEST_COMMAND[@]}" -CAfile "${CA_CERT}" | grep 'Verification: OK')
assert_equal "${RESULT}" 'Verification: OK'
}
@test "last" {
skip 'this test is only there to reliably mark the end for the teardown_file'
skip 'this test is only there to reliably mark the end for the teardown_file'
}

View file

@ -59,10 +59,29 @@ function teardown_file() {
check_ports 'ecdsa' 'modern'
}
# Only ECDSA with RSA fallback is tested.
# There isn't a situation where RSA with ECDSA fallback would make sense.
@test "checking tls: cipher list - ecdsa intermediate, with rsa fallback" {
check_ports 'ecdsa' 'intermediate' 'rsa'
}
@test "checking tls: cipher list - ecdsa modern, with rsa fallback" {
check_ports 'ecdsa' 'modern' 'rsa'
}
function check_ports() {
local KEY_TYPE=$1
local TLS_LEVEL=$2
local RESULTS_PATH="${KEY_TYPE}/${TLS_LEVEL}"
local ALT_KEY_TYPE=$3 # Optional parameter
local KEY_TYPE_LABEL="${KEY_TYPE}"
# This is just to add a `_` delimiter between the two key types for readability
if [[ -n ${ALT_KEY_TYPE} ]]
then
KEY_TYPE_LABEL="${KEY_TYPE}_${ALT_KEY_TYPE}"
fi
local RESULTS_PATH="${KEY_TYPE_LABEL}/${TLS_LEVEL}"
collect_cipherlist_data
@ -81,6 +100,15 @@ function check_ports() {
}
function collect_cipherlist_data() {
local ALT_CERT=()
local ALT_KEY=()
if [[ -n ${ALT_KEY_TYPE} ]]
then
ALT_CERT=(--env SSL_ALT_CERT_PATH="/config/ssl/cert.${ALT_KEY_TYPE}.pem")
ALT_KEY=(--env SSL_ALT_KEY_PATH="/config/ssl/key.${ALT_KEY_TYPE}.pem")
fi
run docker run -d --name tls_test_cipherlists \
--volume "${PRIVATE_CONFIG}/:/tmp/docker-mailserver/" \
--volume "${TLS_CONFIG_VOLUME}" \
@ -89,6 +117,8 @@ function collect_cipherlist_data() {
--env SSL_TYPE="manual" \
--env SSL_CERT_PATH="/config/ssl/cert.${KEY_TYPE}.pem" \
--env SSL_KEY_PATH="/config/ssl/key.${KEY_TYPE}.pem" \
"${ALT_CERT[@]}" \
"${ALT_KEY[@]}" \
--env TLS_LEVEL="${TLS_LEVEL}" \
--network "${NETWORK}" \
--network-alias "${DOMAIN}" \
@ -107,21 +137,20 @@ function collect_cipherlist_data() {
# For non-CI test runs, instead of removing prior test files after this test suite completes,
# they're retained and overwritten by future test runs instead. Useful for inspection.
# `--preference` reduces the test scope to the cipher suites reported as supported by the server. Completes in ~35% of the time.
local TESTSSL_CMD="--quiet --file /config/ssl/testssl.txt --mode parallel --overwrite --preference"
local TESTSSL_CMD=(--quiet --file "/config/ssl/testssl.txt" --mode parallel --overwrite --preference)
# NOTE: Batch testing ports via `--file` doesn't properly bubble up failure.
# If the failure for a test is misleading consider testing a single port with:
# local TESTSSL_CMD="--quiet --jsonfile-pretty ${RESULTS_PATH}/port_${PORT}.json --starttls smtp ${DOMAIN}:${PORT}"
# local TESTSSL_CMD=(--quiet --jsonfile-pretty "${RESULTS_PATH}/port_${PORT}.json" --starttls smtp "${DOMAIN}:${PORT}")
# TODO: Can use `jq` to check for failure when this is resolved: https://github.com/drwetter/testssl.sh/issues/1844
# `--user "<uid>:<gid>"` is a workaround: Avoids `permission denied` write errors for json output, uses `id` to match user uid & gid.
# shellcheck disable=SC2086 # ${TESTSSL_CMD} doesn't work with double quotes
run docker run --rm \
--user "$(id -u):$(id -g)" \
--network "${NETWORK}" \
--volume "${TLS_CONFIG_VOLUME}" \
--volume "${TLS_RESULTS_DIR}/${RESULTS_PATH}/:/output" \
--workdir "/output" \
drwetter/testssl.sh:3.1dev ${TESTSSL_CMD}
drwetter/testssl.sh:3.1dev "${TESTSSL_CMD[@]}"
assert_success
}
@ -153,6 +182,8 @@ function check_cipherlists() {
# Expected cipher lists. Should match `TLS_LEVEL` cipher lists set in `start-mailserver.sh`.
# Excluding Port 25 which uses defaults from Postfix after applying `smtpd_tls_exclude_ciphers` rules.
# NOTE: If a test fails, look at the `check_ports` params, then update the corresponding associative key's value
# with the `actual` error value (assuming an update needs to be made, and not a valid security issue to look into).
function get_cipherlist() {
local TLS_VERSION=$1
@ -165,37 +196,53 @@ function get_cipherlist() {
# Associative array for easy querying of required cipher list
declare -A CIPHER_LIST
# Our TLS v1.0 and v1.1 cipher suites should be the same:
# `intermediate` cipher lists TLS v1.0 and v1.1 cipher suites should be the same:
CIPHER_LIST["rsa_intermediate_TLSv1"]='"ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA"'
CIPHER_LIST["rsa_intermediate_TLSv1_1"]=${CIPHER_LIST["rsa_intermediate_TLSv1"]}
CIPHER_LIST["rsa_intermediate_TLSv1_2"]='"ECDHE-RSA-CHACHA20-POLY1305 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA256 DHE-RSA-AES256-SHA"'
# `modern` doesn't have TLS v1.0 or v1.1 cipher suites:
# `modern` cipher lists shouldn't have TLS v1.0 or v1.1 cipher suites:
CIPHER_LIST["rsa_modern_TLSv1_2"]='"ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-CHACHA20-POLY1305 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384"'
# ECDSA
# ECDSA:
CIPHER_LIST["ecdsa_intermediate_TLSv1"]='"ECDHE-ECDSA-AES128-SHA ECDHE-ECDSA-AES256-SHA"'
CIPHER_LIST["ecdsa_intermediate_TLSv1_1"]=${CIPHER_LIST["ecdsa_intermediate_TLSv1"]}
CIPHER_LIST["ecdsa_intermediate_TLSv1_2"]='"ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-AES128-SHA ECDHE-ECDSA-AES256-SHA384 ECDHE-ECDSA-AES256-SHA"'
CIPHER_LIST["ecdsa_modern_TLSv1_2"]='"ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305"'
# ECDSA + RSA fallback, dual cert support:
CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1"]='"ECDHE-ECDSA-AES128-SHA ECDHE-RSA-AES128-SHA ECDHE-ECDSA-AES256-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA"'
CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_1"]=${CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1"]}
CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_2"]='"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"'
CIPHER_LIST["ecdsa_rsa_modern_TLSv1_2"]='"ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-RSA-CHACHA20-POLY1305 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384"'
# Port 25
# TLSv1 and TLSv1_1 share the same cipher suites as other ports have. The server order differs.
# TLSv1_2 has different server order and ARIA, CCM, DHE+CHACHA20-POLY1305 cipher suites
# TLSv1 and TLSv1_1 share the same cipher suites as other ports have. But the server order differs:
CIPHER_LIST["rsa_intermediate_TLSv1_p25"]='"ECDHE-RSA-AES256-SHA DHE-RSA-AES256-SHA ECDHE-RSA-AES128-SHA DHE-RSA-AES128-SHA"'
CIPHER_LIST["rsa_intermediate_TLSv1_1_p25"]=${CIPHER_LIST["rsa_intermediate_TLSv1_p25"]}
# TLSv1_2 has different server order and also includes ARIA, CCM, DHE+CHACHA20-POLY1305 cipher suites:
CIPHER_LIST["rsa_intermediate_TLSv1_2_p25"]='"ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-CHACHA20-POLY1305 DHE-RSA-CHACHA20-POLY1305 DHE-RSA-AES256-CCM8 DHE-RSA-AES256-CCM ECDHE-ARIA256-GCM-SHA384 DHE-RSA-ARIA256-GCM-SHA384 ECDHE-RSA-AES256-SHA384 DHE-RSA-AES256-SHA256 ECDHE-RSA-AES256-SHA DHE-RSA-AES256-SHA ARIA256-GCM-SHA384 ECDHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES128-CCM8 DHE-RSA-AES128-CCM ECDHE-ARIA128-GCM-SHA256 DHE-RSA-ARIA128-GCM-SHA256 ECDHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA256 ECDHE-RSA-AES128-SHA DHE-RSA-AES128-SHA ARIA128-GCM-SHA256"'
# Port 25 is unaffected by `TLS_LEVEL` profiles (other than min TLS version), it has the same TLS v1.2 cipher list under both:
CIPHER_LIST["rsa_modern_TLSv1_2_p25"]=${CIPHER_LIST["rsa_intermediate_TLSv1_2_p25"]}
# ECDSA
# ECDSA (Port 25):
CIPHER_LIST["ecdsa_intermediate_TLSv1_p25"]='"ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-AES128-SHA"'
CIPHER_LIST["ecdsa_intermediate_TLSv1_1_p25"]=${CIPHER_LIST["ecdsa_intermediate_TLSv1_p25"]}
CIPHER_LIST["ecdsa_intermediate_TLSv1_2_p25"]='"ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES256-CCM8 ECDHE-ECDSA-AES256-CCM ECDHE-ECDSA-ARIA256-GCM-SHA384 ECDHE-ECDSA-AES256-SHA384 ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-CCM8 ECDHE-ECDSA-AES128-CCM ECDHE-ECDSA-ARIA128-GCM-SHA256 ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-AES128-SHA"'
CIPHER_LIST["ecdsa_modern_TLSv1_2_p25"]=${CIPHER_LIST["ecdsa_intermediate_TLSv1_2_p25"]}
local TARGET_QUERY="${KEY_TYPE}_${TLS_LEVEL}_${TLS_VERSION}"
# ECDSA + RSA fallback, dual cert support (Port 25):
CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_p25"]='"ECDHE-ECDSA-AES256-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES256-SHA ECDHE-ECDSA-AES128-SHA ECDHE-RSA-AES128-SHA DHE-RSA-AES128-SHA"'
CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_1_p25"]=${CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_p25"]}
CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_2_p25"]='"ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-RSA-CHACHA20-POLY1305 DHE-RSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES256-CCM8 ECDHE-ECDSA-AES256-CCM DHE-RSA-AES256-CCM8 DHE-RSA-AES256-CCM ECDHE-ECDSA-ARIA256-GCM-SHA384 ECDHE-ARIA256-GCM-SHA384 DHE-RSA-ARIA256-GCM-SHA384 ECDHE-ECDSA-AES256-SHA384 ECDHE-RSA-AES256-SHA384 DHE-RSA-AES256-SHA256 ECDHE-ECDSA-AES256-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES256-SHA ARIA256-GCM-SHA384 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-CCM8 ECDHE-ECDSA-AES128-CCM DHE-RSA-AES128-CCM8 DHE-RSA-AES128-CCM ECDHE-ECDSA-ARIA128-GCM-SHA256 ECDHE-ARIA128-GCM-SHA256 DHE-RSA-ARIA128-GCM-SHA256 ECDHE-ECDSA-AES128-SHA256 ECDHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA256 ECDHE-ECDSA-AES128-SHA ECDHE-RSA-AES128-SHA DHE-RSA-AES128-SHA ARIA128-GCM-SHA256"'
CIPHER_LIST["ecdsa_rsa_modern_TLSv1_2_p25"]=${CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_2_p25"]}
local TARGET_QUERY="${KEY_TYPE_LABEL}_${TLS_LEVEL}_${TLS_VERSION}"
echo "${CIPHER_LIST[${TARGET_QUERY}]}"
fi
}

View file

@ -7,7 +7,7 @@ These certificates for usage with TLS have been generated via the [Smallstep `st
`Certificate Details` sections are the output of: `step certificate inspect cert.<key type>.pem`.
---
<!-- markdownlint-disable MD033 MD040 -->
**RSA (2048-bit) - self-signed:**
```sh
@ -21,6 +21,7 @@ step certificate create "Smallstep self-signed" cert.rsa.pem key.rsa.pem \
--kty RSA --size 2048
```
<!-- markdownlint-disable MD033 MD040 -->
<details>
<summary>Certificate Details:</summary>
@ -144,3 +145,145 @@ Certificate:
</details>
<!-- markdownlint-enable MD033 MD040 -->
---
`self-signed` certs lacks a chain of trust for verifying a certificate. See `test/mail_ssl_manual.bats` which covers verification test.
The minimal setup to satisfy verification is adding a Root CA (self-signed) that is used to sign the server certificate (leaf cert):
Create an ECDSA Root CA cert:
```sh
step certificate create "Smallstep Root CA" ca-cert.ecdsa.pem ca-key.ecdsa.pem \
--no-password --insecure \
--profile root-ca \
--not-before "2021-01-01T00:00:00+00:00" \
--not-after "2031-01-01T00:00:00+00:00" \
--san "example.test" \
--san "mail.example.test" \
--kty EC --crv P-256
```
Create an ECDSA Leaf cert, signed with the Root CA key we just created:
```sh
step certificate create "Smallstep Leaf" cert.ecdsa.pem key.ecdsa.pem \
--no-password --insecure \
--profile leaf \
--ca ca-cert.ecdsa.pem \
--ca-key ca-key.ecdsa.pem \
--not-before "2021-01-01T00:00:00+00:00" \
--not-after "2031-01-01T00:00:00+00:00" \
--san "example.test" \
--san "mail.example.test" \
--kty EC --crv P-256
```
The Root CA certificate does not need to have the same key type as the Leaf certificate, you can mix and match if necessary (eg: an ECDSA and an RSA leaf certs with shared ECDSA Root CA cert).
<!-- markdownlint-disable MD033 MD040 -->
<details>
<summary>Certificate Details (signed by Root CA key):</summary>
`step certificate inspect with_ca/ecdsa/cert.ecdsa.pem`:
```
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 28540880372304824564361820670143583738 (0x1578c60b9eedca127fe041712f9d55fa)
Signature Algorithm: ECDSA-SHA256
Issuer: CN=Smallstep Root CA
Validity
Not Before: Jan 1 00:00:00 2021 UTC
Not After : Jan 1 00:00:00 2031 UTC
Subject: CN=Smallstep Leaf
Subject Public Key Info:
Public Key Algorithm: ECDSA
Public-Key: (256 bit)
X:
b6:64:18:5f:f6:3f:b6:b1:da:09:00:27:e9:70:4e:
8e:11:c4:58:8d:02:a2:46:f6:5b:d5:12:9b:ea:6a:
e4:39
Y:
87:56:d8:43:6b:4d:5d:4a:44:73:d2:81:34:1d:cd:
de:53:ed:62:c4:61:76:c6:bf:96:0a:0a:8e:10:fa:
c2:63
Curve: P-256
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature
X509v3 Extended Key Usage:
Server Authentication, Client Authentication
X509v3 Subject Key Identifier:
48:C4:A2:B2:31:9B:9C:3D:4D:BD:58:45:60:F0:C6:16:EB:74:C0:3B
X509v3 Authority Key Identifier:
keyid:3F:3D:65:1A:72:82:16:C6:20:E8:B6:FC:1B:2E:6D:A4:9C:2C:92:78
X509v3 Subject Alternative Name:
DNS:example.test, DNS:mail.example.test
Signature Algorithm: ECDSA-SHA256
30:46:02:21:00:b6:dc:7d:ba:f6:d9:b1:3f:28:4d:6d:4c:a4:
e9:c5:24:80:d4:6c:a5:fc:9f:74:4e:9a:bb:5b:ca:8a:5e:dd:
32:02:21:00:e2:c8:8b:1b:be:a2:f9:5f:cd:41:8c:0a:75:71:
ca:e9:be:65:d1:ca:5e:50:77:f7:8a:c0:f8:03:77:1b:53:0a
```
</details>
<details>
<summary>Root CA Certificate Details (self-signed):</summary>
`step certificate inspect with_ca/ecdsa/ca-cert.ecdsa.pem`:
```
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 83158808788179848488617675347018882219 (0x3e8fcdd2d80ab546924c05b4d9339cab)
Signature Algorithm: ECDSA-SHA256
Issuer: CN=Smallstep Root CA
Validity
Not Before: Jan 1 00:00:00 2021 UTC
Not After : Jan 1 00:00:00 2031 UTC
Subject: CN=Smallstep Root CA
Subject Public Key Info:
Public Key Algorithm: ECDSA
Public-Key: (256 bit)
X:
76:30:c0:21:d2:6c:6b:ca:de:be:1d:c3:5c:67:08:
93:bf:73:53:2a:23:5d:d8:06:2a:8b:09:bc:39:fd:
0b:0d
Y:
a7:74:1f:7c:b9:95:73:6c:ba:00:00:d7:52:06:0c:
e9:00:c8:aa:bb:e1:50:e7:ec:ff:bf:e5:30:bb:9b:
18:07
Curve: P-256
X509v3 extensions:
X509v3 Key Usage: critical
Certificate Sign, CRL Sign
X509v3 Basic Constraints: critical
CA:TRUE, pathlen:1
X509v3 Subject Key Identifier:
3F:3D:65:1A:72:82:16:C6:20:E8:B6:FC:1B:2E:6D:A4:9C:2C:92:78
Signature Algorithm: ECDSA-SHA256
30:45:02:21:00:bf:d7:51:c7:7b:67:41:90:ac:c5:89:cd:04:
60:7d:6b:da:8d:75:c2:c6:1c:18:93:82:79:96:35:19:a4:ea:
2f:02:20:5a:bc:95:3b:de:f6:8b:00:fd:1a:69:81:57:b5:b6:
91:0f:10:ef:2b:b2:39:83:c0:3c:a0:26:21:51:4b:40:3c
```
</details>
<!-- markdownlint-enable MD033 MD040 -->
---
When bundling chain of trust into a single certificate file (eg: `fullchain.pem`), starting with the server cert, include any additional parent certificates in the chain - but do not add the final Root CA cert; otherwise you'll get a related error with not being able to verify trust:
```sh
$ openssl s_client -connect mail.example.test:587 -starttls smtp
# Verification error: self signed certificate in certificate chain
```
Thus, the minimal bundle would be `leaf->intermediate` (`fullchain.pem`) with separate Root CA cert.

View file

@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBezCCASGgAwIBAgIQPo/N0tgKtUaSTAW02TOcqzAKBggqhkjOPQQDAjAcMRow
GAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTAeFw0yMTAxMDEwMDAwMDBaFw0zMTAx
MDEwMDAwMDBaMBwxGjAYBgNVBAMTEVNtYWxsc3RlcCBSb290IENBMFkwEwYHKoZI
zj0CAQYIKoZIzj0DAQcDQgAEdjDAIdJsa8revh3DXGcIk79zUyojXdgGKosJvDn9
Cw2ndB98uZVzbLoAANdSBgzpAMiqu+FQ5+z/v+Uwu5sYB6NFMEMwDgYDVR0PAQH/
BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFD89ZRpyghbGIOi2
/BsubaScLJJ4MAoGCCqGSM49BAMCA0gAMEUCIQC/11HHe2dBkKzFic0EYH1r2o11
wsYcGJOCeZY1GaTqLwIgWryVO972iwD9GmmBV7W2kQ8Q7yuyOYPAPKAmIVFLQDw=
-----END CERTIFICATE-----

View file

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEICSObYiRorZzuZW+17D/FqsDztCiw0bnS0NPG1MaXh7moAoGCCqGSM49
AwEHoUQDQgAEdjDAIdJsa8revh3DXGcIk79zUyojXdgGKosJvDn9Cw2ndB98uZVz
bLoAANdSBgzpAMiqu+FQ5+z/v+Uwu5sYBw==
-----END EC PRIVATE KEY-----

View file

@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE-----
MIIB0zCCAXigAwIBAgIQFXjGC57tyhJ/4EFxL51V+jAKBggqhkjOPQQDAjAcMRow
GAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTAeFw0yMTAxMDEwMDAwMDBaFw0zMTAx
MDEwMDAwMDBaMBkxFzAVBgNVBAMTDlNtYWxsc3RlcCBMZWFmMFkwEwYHKoZIzj0C
AQYIKoZIzj0DAQcDQgAEtmQYX/Y/trHaCQAn6XBOjhHEWI0Cokb2W9USm+pq5DmH
VthDa01dSkRz0oE0Hc3eU+1ixGF2xr+WCgqOEPrCY6OBnjCBmzAOBgNVHQ8BAf8E
BAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBRI
xKKyMZucPU29WEVg8MYW63TAOzAfBgNVHSMEGDAWgBQ/PWUacoIWxiDotvwbLm2k
nCySeDAqBgNVHREEIzAhggxleGFtcGxlLnRlc3SCEW1haWwuZXhhbXBsZS50ZXN0
MAoGCCqGSM49BAMCA0kAMEYCIQC23H269tmxPyhNbUyk6cUkgNRspfyfdE6au1vK
il7dMgIhAOLIixu+ovlfzUGMCnVxyum+ZdHKXlB394rA+AN3G1MK
-----END CERTIFICATE-----

View file

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICnTCCAkOgAwIBAgIQa9dHWr1hoQZ5sVU5yjIGlDAKBggqhkjOPQQDAjAcMRow
GAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTAeFw0yMTAxMDEwMDAwMDBaFw0zMTAx
MDEwMDAwMDBaMBkxFzAVBgNVBAMTDlNtYWxsc3RlcCBMZWFmMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAraT2GdokyQRFFESQ0tQtS+lq952GoUqEhKwv
O4NIsYHtfhboUbWdjUQcI9reiBVeiv0o/BmwCZUm31dqT0tb1fF1GvEkapZufCy8
EXe84TYeJGtek34tTWd9uxAYpSI8igVfvlrGKMPzphQlgSXoH7lyrFBjzzBv96il
9PzH7iEoYzlwBx3pHIHLeyivzFEnyvVUlKDNyaQkwroVt6/8CyAfzn46mvujutEh
owFJGgQxnbiloqJmk+BYHKw9BepbUsqB1xIv5ASUlPZSgBjR59/SfJCTV2TFsF9Q
B+L0Ev2X6Vv9va5Hlj2FszHraxV82R/vJ90pMxVfffHloe3qTwIDAQABo4GeMIGb
MA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
HQYDVR0OBBYEFFOlJeT6UG8UCSlh2f5mWlaIkPymMB8GA1UdIwQYMBaAFD89ZRpy
ghbGIOi2/BsubaScLJJ4MCoGA1UdEQQjMCGCDGV4YW1wbGUudGVzdIIRbWFpbC5l
eGFtcGxlLnRlc3QwCgYIKoZIzj0EAwIDSAAwRQIhALcFzitAGXHJ+Dnv0z8vMWMw
iW09cFkrE6nkDtKWwNhIAiBUQ3buC5dZz7UNES/54OAeMGTagjqOIyZLF0QE7ls+
dQ==
-----END CERTIFICATE-----

View file

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIH8zQfeaX0H1KEwQi4NdV5Az1TsQL6HfipqhObBEBQJuoAoGCCqGSM49
AwEHoUQDQgAEtmQYX/Y/trHaCQAn6XBOjhHEWI0Cokb2W9USm+pq5DmHVthDa01d
SkRz0oE0Hc3eU+1ixGF2xr+WCgqOEPrCYw==
-----END EC PRIVATE KEY-----

View file

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAraT2GdokyQRFFESQ0tQtS+lq952GoUqEhKwvO4NIsYHtfhbo
UbWdjUQcI9reiBVeiv0o/BmwCZUm31dqT0tb1fF1GvEkapZufCy8EXe84TYeJGte
k34tTWd9uxAYpSI8igVfvlrGKMPzphQlgSXoH7lyrFBjzzBv96il9PzH7iEoYzlw
Bx3pHIHLeyivzFEnyvVUlKDNyaQkwroVt6/8CyAfzn46mvujutEhowFJGgQxnbil
oqJmk+BYHKw9BepbUsqB1xIv5ASUlPZSgBjR59/SfJCTV2TFsF9QB+L0Ev2X6Vv9
va5Hlj2FszHraxV82R/vJ90pMxVfffHloe3qTwIDAQABAoIBAQClg0uhMLFHee5u
dzyj+OKELSVsiJY/P0w5OfZ7f5PWvsWlHiirWbWnZXh9JK5ggB3x1YUvZzzIpYa7
9BK9KlOLBfBdkeToRCNj8TACZmN/N5pblIw9glOkKDVMDPewz4Vs+dpdEyE20jB3
6VQmWf973cRUQvwgDUdshTBK6HeZnid0SoRNSDA5ygYNU/q6LLaEWdingHw7A3GP
kwJSgB9ZCYGubgwwu+XCpAdEcX/sc24JDyLFyrp9ei/Qs2IkIAQfLoCmn2gNsD2P
77HfhkOg43bSiRBqxxfdbF+Vzta0jse8AzFY3SCsVF59skviKgmPOt55sVUhjQQc
6jzz0CShAoGBANO2bgaEbjO6mL1ye2Dh6acx9DT0N6XeQSTTkllfp4/De8vnTlqL
z4hChjLN7pO1Nb9svytCFhE7YoFmm9PsKkuMjs/YnsFpZC9fi8uZ3WU3Koml3sZ8
askwQ2dBryVnYeOtnLKT4drDHwL7302AOzZz2pZ/PUKmQhTi/QBrhEg1AoGBANH3
6a+8gBUbHmmnVKQ+4GjoA6TTXrtBoODwsZq3hmQUMvaU2WDTlej0XAGIuwYvEycc
tW8UOBNIkl+CoMh5OiJ/bHaduBrOyKytw5Ahp2vO/RWarrTTJJiBNNJS+GYwDXuB
F2ndseVaGzRFIU1Um8WcnB7jvIKYB2mUOeIfU+DzAoGBAKk1uaKD3gCCNunTwkCg
zzrOdjyMpJ1xked5IhNEnIwO1bcW3E0KSYjCgrfAV9q+joK8y94rJ7AGIqkB9bHf
o75WTR5aKCi1r1kdqIHGLGllOh5h8Df74O2EIZs5qF7gziBG9mLUR0Oth+++l689
uhW/awm2EKRgdZh0A3p+dG+xAoGBALeGLODpLz7DdGK+2nGxb67iVQUFp4CfTYIv
f92O2k2xhhYXZqWGazqyE5VXsLkn/mKqaj+L+bOJND99SxHPPCwZGnHXS6tK4QZl
31CkydSmmHoAuQHep9cQ4F2SHgIbsD0qSY/EMhEZIDwgzInuETW5vJAXWJcBUUFM
SQfHgCZXAoGAKS4/ak3IDP4PwyAc0KvN2J7S7EyVpgjNGzCkznTFoaNbYj0w/dwA
9Y13LguGDxn1CcdHqwBMzgjJK+DCmoF9wSQxux5tClzMr24oN+XOn0xiM3ppTXzQ
DfB0x3N8z6/KnqA4xrc6mSlZiMb9Xcn2E0HdO4iaTjNiL5tzwU7IxNc=
-----END RSA PRIVATE KEY-----

View file

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDCDCCAfCgAwIBAgIRANPCP+3+4OKj3S2gIsfQcoIwDQYJKoZIhvcNAQELBQAw
HDEaMBgGA1UEAxMRU21hbGxzdGVwIFJvb3QgQ0EwHhcNMjEwMTAxMDAwMDAwWhcN
MzEwMTAxMDAwMDAwWjAcMRowGAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKuYF7paXiAvIK8IIE6rnNMSuS94
z8+1md2a6DnijBiwG/DcB2e1wxBab/kK+XNsPWI26FLUxqc9zGZ0Nn7s7DOT6C52
43m4vJUrTthsFdLzW9SFCbMFCikYhbOSqfj06oVHRstkAI+WgyX6QrnrSV+h0imk
gZlwjk+oKpkE6rS8zCGgXZMqi7/06qiTwXCVStMnEnX9FuNTbMjgz6bVQH6FOE9e
8QbykSUjWvE+x6Rk5gUpbFGV26Bb62zH6zF9FY6ECsdsJJlALJ7e5w6MTfSL23u8
cRcBhtBaQuHkFg9G6JFigQwbm0DQf7o7crdMJY/paWbCuRhxXx3YGZFOU1sCAwEA
AaNFMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0O
BBYEFB3P7cwUTP3cQLjy3SvvnSYQLz/DMA0GCSqGSIb3DQEBCwUAA4IBAQBcSu9x
eGVGMjfxOmX1S+MyMJNPWKJuBlW5cX80h7mBIVIKnamJjMBDGu8WJFnaFdch5uwr
KIlXbDmFJjxBb3id/6W3BC09Ze6fjtY+rPRPkCi5L5CbLs6KU1V2X/xoMhg15upH
e29Giz2gK/Zd9/ewxa+sNRx2mk9fhIzgLA55q34OkhEdi0dJFlyzNHw+/6eYICPd
hPv+8z3dreuyXqKBO+M/QqLXHqonvZWbJzeWCqetZn/h9WFM2WXMzRTR/GjaUIQP
/ZPLXm5P4YMtAEbG+PfHuzC0bwUmj5FTGJ7I6v9Y32KS+P94KlsmHOJICqSwLQWo
6MbojAT5BXYzHzHq
-----END CERTIFICATE-----

View file

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAq5gXulpeIC8grwggTquc0xK5L3jPz7WZ3ZroOeKMGLAb8NwH
Z7XDEFpv+Qr5c2w9YjboUtTGpz3MZnQ2fuzsM5PoLnbjebi8lStO2GwV0vNb1IUJ
swUKKRiFs5Kp+PTqhUdGy2QAj5aDJfpCuetJX6HSKaSBmXCOT6gqmQTqtLzMIaBd
kyqLv/TqqJPBcJVK0ycSdf0W41NsyODPptVAfoU4T17xBvKRJSNa8T7HpGTmBSls
UZXboFvrbMfrMX0VjoQKx2wkmUAsnt7nDoxN9Ivbe7xxFwGG0FpC4eQWD0bokWKB
DBubQNB/ujtyt0wlj+lpZsK5GHFfHdgZkU5TWwIDAQABAoIBAEVzKF9fRetmx6av
9kuA/9caI+OH1SwvRxg/IzCSdbtkQ9rHYyPhmBKdV+aHP5EsxOdka8Fipwh3Zawt
6wSSJcN2YKm1qrnQQFtrjNzhWi/oGUm5ksRnDMa3Sx30BWFi1yycPZYCYou7Elej
o7AV/7t6BlKeZ2nP+XlaCeum5svBWHNyeMcRy4d9XF2BkIQ5DcBp0CuVkJ8ERXw7
l0sYSFBN8lu2amI+nwwbjPq636mxTUcMlQ78kHj8DT3YGuzTDBeIOL0nFeR/oeoL
hsst3Pppf72c+llaEfDqI08j2xq+Ebg5tgFGZcbZeus5A33PiQkPHC8plRnIblov
9GzanAECgYEAzHoOkfwtjQz5C/X+pDdiVsegZwzetQMIyX3g50GifnH752t2yDu1
NXI5MUanxEKGaVJpdgq4xax9VSyJH6i92SPJ0qF+8UeXoq9LNitrqh4Xdj6zDfhO
b1OYpokcwzIFlIiEfdhFrXR2nVk3l2ObpCxrM1yrPB8eBHiQPb5BCVsCgYEA1tTp
dKLh/CERPT4j+1IQmsuSPtbxbLprxoZfn8DSZ8jb8ZtGCU4Z6nkHvjnHECe1+yLc
Qi23TmXJvOyzVh4SX3SASi7JwrPJlK5KnPNV1hbX5gsQqiP0mgzFlbulB8WH2vTi
i9/RRE9Td+ssdmBVFw4KC8iUx0kC0pN2I+lf/gECgYEAkrNDPtOJLrhO+Zjcv56I
rC/+0dA2+/EYXc81C1VYqpVGoGrGRrQntxejFspVVQZRfsDErw7UL+Yn6XLRUmhz
BJWbl8WL2Ll/fhV7xXpzfRKnzIsBVZV/WHnGfDzE6Po7bKxhOhRvMRnse8lli/TH
1oqTwr7Kj5DXrWd+PP4BlR8CgYEAh1VG4AAABnm6R29O8XJoHWuibE2xdsIVRNG/
iX7JzTF+RLyBKTl3H/swgogZO7cYb+UtBc+QyyVaKdaevuBgyJs2egTxeuMRWMVX
IXo1F5H6XeOYkuXln+ntyn6T3spz494inOTZCoRCW6fdsZDL6aMhdvzbpJL84TSd
1s+cdgECgYEApBsdab9aEDFDaqauA9wT9OtmFvLMsx0+fBy0Q3sU2NNuoIbztbdu
xRV9uRBH4M/S+eWq4zV/aVoroG/aEgJTepxw840avi2mieUVRr0TPjcA2Z0FsYd0
EAN4XtdSD6ja4fTpju2edz5nKIDtJOVrlCMxwHUXQedrE5KgWjYBaaE=
-----END RSA PRIVATE KEY-----

View file

@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE-----
MIICkzCCAXugAwIBAgIQXv4WNC7ySKm1D3pp7mJBrjANBgkqhkiG9w0BAQsFADAc
MRowGAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTAeFw0yMTAxMDEwMDAwMDBaFw0z
MTAxMDEwMDAwMDBaMBkxFzAVBgNVBAMTDlNtYWxsc3RlcCBMZWFmMFkwEwYHKoZI
zj0CAQYIKoZIzj0DAQcDQgAE34c1Bax+01yot5EyL7gm8jAjoLVvmfOheirlAWsT
61UWUClY6PvpdLOLvDkFSolKkDNg8FC2vZ57H5bM2G7yMqOBnjCBmzAOBgNVHQ8B
Af8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQW
BBRT0qcu0mKGD6X/6Q1u5h09uys3vDAfBgNVHSMEGDAWgBQdz+3MFEz93EC48t0r
750mEC8/wzAqBgNVHREEIzAhggxleGFtcGxlLnRlc3SCEW1haWwuZXhhbXBsZS50
ZXN0MA0GCSqGSIb3DQEBCwUAA4IBAQAEzx3LO3g8NOE7P+5i3Y0iKv+QG6UMRteR
vgImc4l5VnSa8BjyAOPvYNEI9gFUeIxCslMrc1Wh4rfHKymoQk91KQGkMAO3cbIa
axyXdMXNllwOm2FIqTFrK0D2pZ5KSGnQmNGfj6bMl3hHHc0QOV7bgMkSddbms1PY
HxGfFO5UlseLbvCHj8tofamCTr+t9i5ioAOpoaOTOyq34pIgY7AnDE4/zMP/aCqi
dxD6NO8gcebjuPrF4oi+8wEqSl5/jPNQu/wfup4mPjDl1PenSyue2PfuHebHOeEh
39pZNq+6UfsCmw11mqiDNa9XqaRBRMdEtzMiEs4MB03RBLt97KOM
-----END CERTIFICATE-----

View file

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDXzCCAkegAwIBAgIRAKx4AavefugBgIJG3zel3P4wDQYJKoZIhvcNAQELBQAw
HDEaMBgGA1UEAxMRU21hbGxzdGVwIFJvb3QgQ0EwHhcNMjEwMTAxMDAwMDAwWhcN
MzEwMTAxMDAwMDAwWjAZMRcwFQYDVQQDEw5TbWFsbHN0ZXAgTGVhZjCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAOAUZVzOK9Apmcjfq5x9o48EQLmZm/Xb
hbWQGV093yIecsWJD3Rx/As5fo4TLdzS8Xao6dlXqzlVRNjvCmPATs6dJnVP1tm/
aqeVFwKHqh5lhHqx93FYKTPbMcpQHGfVNY8rD3/aLrTbcWiqGd9wrKUAUJO8LOS4
HhFsjcUYwulXkosHiYioZImksq1Z/cu1L3fzUuPH5WyRk8Nh+aXUuXpHGxTS1djS
p7d3OjEvK5Ob8AJQjkY3VS90KlxqQ94epPXPxp0hdYpOxbswXU0FMF4SeO1yc/QW
KeqKtPMvRwg+IXwkTHqDRyZMHn5xrvAOaV1jkp+mjDptoQpUKzB0OXUCAwEAAaOB
njCBmzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUF
BwMCMB0GA1UdDgQWBBQGnpb0nk82jVI3JImnfdHR7DYjADAfBgNVHSMEGDAWgBQd
z+3MFEz93EC48t0r750mEC8/wzAqBgNVHREEIzAhggxleGFtcGxlLnRlc3SCEW1h
aWwuZXhhbXBsZS50ZXN0MA0GCSqGSIb3DQEBCwUAA4IBAQAKNre3khwoMoL0LoQG
UF8HVi5OxzzKCzueZQMU8fj/MXQhpPWAkXKa4vXwCDvVRBCC1j4u+xSeiqXvMVcR
n7QyxvYKJRnQ0k/x1zp8N6eed2tOFOz+gyHkoNSr/l9fQsAhqoL0FeVatqczI0co
DGg5ux5bjZwllFYw6LRIuhtZ4BxIQO4GC5pysrvjXb782v0iAowQHL3yC0x/Eyfr
ca/qovVST8zoWYf/1pQ/7Kp9do8VraB4dyr5r/zAy0GHPfia8qL864wTqGcuRnW3
1y2BOcKLgnTNCFp2ncWodmObsxom8KgUAVyW06cDx4XI1wa2FSx4G5mehBgSd/mQ
1Avy
-----END CERTIFICATE-----

View file

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIB/MzM0qKMD66JmEKO+OGkgWNJ9Gtheytm62rgxSIprLoAoGCCqGSM49
AwEHoUQDQgAE34c1Bax+01yot5EyL7gm8jAjoLVvmfOheirlAWsT61UWUClY6Pvp
dLOLvDkFSolKkDNg8FC2vZ57H5bM2G7yMg==
-----END EC PRIVATE KEY-----

View file

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA4BRlXM4r0CmZyN+rnH2jjwRAuZmb9duFtZAZXT3fIh5yxYkP
dHH8Czl+jhMt3NLxdqjp2VerOVVE2O8KY8BOzp0mdU/W2b9qp5UXAoeqHmWEerH3
cVgpM9sxylAcZ9U1jysPf9outNtxaKoZ33CspQBQk7ws5LgeEWyNxRjC6VeSiweJ
iKhkiaSyrVn9y7Uvd/NS48flbJGTw2H5pdS5ekcbFNLV2NKnt3c6MS8rk5vwAlCO
RjdVL3QqXGpD3h6k9c/GnSF1ik7FuzBdTQUwXhJ47XJz9BYp6oq08y9HCD4hfCRM
eoNHJkwefnGu8A5pXWOSn6aMOm2hClQrMHQ5dQIDAQABAoIBAH6Yx1ORX4txlWK5
i1kUWm2Yd4DkWgqjBX24dbwKEqBSF4Gml8awBzfIOcnG6ChUPPtPHx8duqzfkdAF
7RbCNUPh4TJx8u5+iKE5SBCz2Nbnf9tZ5HRy3IRhmFW2wPWgrWu/ZjhTagPf3sjF
IWztWXy3Gs78h1iI9OPfMpFiFeyB8LEE1w4nb/iSSUvFue/VZ0aDS5eOmUFVzjxy
xNfHZ33QnmRYbsJ58oF2Fr+3fTmltM406y+Tg3+Dao+Mpf41NKq+1r58PnKpUjfH
cLO5k4/Mqbnk4xi/ftzApIew1A6ClO9xwA3+oYE/S0LQ8JHEVajG1aMw8PEXWbOJ
wzcXwEECgYEA7NZQDQHjCCzdmECITSr3I3tuXbmnv29RS4sAepn9fS4OVosnntrc
YnRGriuFIZpyrr/3R5DwTgO61VeBAqvJDYtVjj6zyT9f87fdm8gJeDW7T0G24vt6
0H4KG8ws8+I/FjMj7wiC4yN4/Hcyd94squh+/9NjdTvwfvS6frwnQR0CgYEA8jXV
SRfBYJ8U8VBMZxfOmvEtJfxaahtnJGBBEwDcCfgB+rIjVQGw0aZONPKilD8jinAt
UiBwyBDkHaUL14s6H23+QS6am6Mr9lTz+YPqpYWG0VjGBv8kuHZ50EhlBJFadzMb
VVR5R5FDP3ChMQy7Q6e8RoIK5DQD86vKjQRegjkCgYAlERGsR3xR3ju8RXVPpobR
bdMDJjhj1LdDfHjRt2IeAmRKFTNZQGW3nv0k6zjF3pdOVEsOT1fczeai1zQgx+QK
k6ELRzL6L0oEKeWsKO2ae8ZaDC3kbnl1QhSw7w6mCOXYwp5AHfPmOroHwVwLuKED
Cqo9vcbWJVBpfkHl7eqy3QKBgQCHOZDrbuzSsd4yX79YK0146b9oHryn0sbB409R
ecBffGw2d7AMLJZ4Zd3x56jnFV0VVE2pNV1iBTQmbNfwrdV0aKdz4r4EuJO5wnI3
0vN1F9hOFr7wdxAcQGD/7PshErmsJQdUm4Xec/ZUe+Ayj0YZnpMZ1k6YW4X9S+MY
2eCd2QKBgQDQRh7HHS3n7PAwwtUDKR9oEFWE0dLOr+RWSeFsXz10gxUbQuHvLXU4
nOCGA+Add6995PAs9xcnA/Ewju/l3YBgiQPvmLqEnpSZUiRoKQcTE+sAd9FCNB/4
lORhokK5KlIkxUQISAZ65p0awQe0yyfOD1VJmcvALF1FRwWyz5M1TA==
-----END RSA PRIVATE KEY-----