mirror of
https://github.com/docker-mailserver/docker-mailserver.git
synced 2025-08-03 17:44:49 +02:00
tests: Adjust parallel tests
- The usual serial to parallel test conversion to utilize the `setup.bash` common setup structure, and adding a `TEST_PREFIX` var for each test case to leverage. - Standardize on parallel test naming conventions for variables / values. - More consistent use of `bash -c` instead of `/bin/bash -c` or `/bin/sh -c`. - Using the `_run_in_container` helper instead of `run docker exec ${CONTAINER_NAME}`. - Updates tests to use the `check_if_process_is_running` helper. --- chore: Revise inline docs for the `ssl_letsencrypt` test - Moves the override to be in closer proximity to the `initial_setup` call, and better communicates the intent to override. - Removes top comment block that is no longer providing value or correct information to maintainers. - Revised `acme.json` test case inline doc comments.
This commit is contained in:
parent
306592fcad
commit
2ec6c4abc0
17 changed files with 623 additions and 591 deletions
|
@ -1,81 +1,128 @@
|
|||
#!/usr/bin/env bats
|
||||
load "${REPOSITORY_ROOT}/test/test_helper/common"
|
||||
# Globals ${BATS_TMPDIR} and ${NAME}
|
||||
# `${NAME}` defaults to `mailserver-testing:ci`
|
||||
load "${REPOSITORY_ROOT}/test/helper/setup"
|
||||
load "${REPOSITORY_ROOT}/test/helper/common"
|
||||
|
||||
function teardown() {
|
||||
docker rm -f tls_test_cipherlists
|
||||
}
|
||||
TEST_NAME_PREFIX='[Security] TLS (cipher lists):'
|
||||
CONTAINER_PREFIX='dms-test_tls-cipherlists'
|
||||
|
||||
# NOTE: Tests cases here cannot be run concurrently:
|
||||
# - The `testssl.txt` file configures `testssl.sh` to connect to `example.test` (TEST_DOMAIN)
|
||||
# and this is set as a network alias to the DMS container being tested.
|
||||
# - If multiple containers are active with this alias, the connection is not deterministic and will result
|
||||
# in comparing the wrong results for a given variant.
|
||||
|
||||
function setup_file() {
|
||||
export DOMAIN="example.test"
|
||||
export NETWORK="test-network"
|
||||
export TEST_DOMAIN='example.test'
|
||||
export TEST_FQDN="mail.${TEST_DOMAIN}"
|
||||
export TEST_NETWORK='test-network'
|
||||
|
||||
# Shared config for TLS testing (read-only)
|
||||
# Contains various certs for testing TLS support (read-only):
|
||||
export TLS_CONFIG_VOLUME
|
||||
TLS_CONFIG_VOLUME="$(pwd)/test/test-files/ssl/${DOMAIN}/:/config/ssl/:ro"
|
||||
# `${BATS_TMPDIR}` maps to `/tmp`
|
||||
export TLS_RESULTS_DIR="${BATS_TMPDIR}/results"
|
||||
TLS_CONFIG_VOLUME="${PWD}/test/test-files/ssl/${TEST_DOMAIN}/:/config/ssl/:ro"
|
||||
|
||||
# NOTE: If the network already exists, test will fail to start.
|
||||
docker network create "${NETWORK}"
|
||||
|
||||
# Copies all of `./test/config/` to specific directory for testing
|
||||
# `${PRIVATE_CONFIG}` becomes `$(pwd)/test/duplicate_configs/<bats test filename>`
|
||||
export PRIVATE_CONFIG
|
||||
PRIVATE_CONFIG=$(duplicate_config_for_container .)
|
||||
# Used for connecting testssl and DMS containers via network name `TEST_DOMAIN`:
|
||||
# NOTE: If the network already exists, the test will fail to start
|
||||
docker network create "${TEST_NETWORK}"
|
||||
|
||||
# Pull `testssl.sh` image in advance to avoid it interfering with the `run` captured output.
|
||||
# Only interferes (potential test failure) with `assert_output` not `assert_success`?
|
||||
docker pull drwetter/testssl.sh:3.1dev
|
||||
|
||||
# Only used in `should_support_expected_cipherlists()` to set a storage location for `testssl.sh` JSON output:
|
||||
# `${BATS_TMPDIR}` maps to `/tmp`: https://bats-core.readthedocs.io/en/v1.8.2/writing-tests.html#special-variables
|
||||
export TLS_RESULTS_DIR="${BATS_TMPDIR}/results"
|
||||
}
|
||||
|
||||
function teardown_file() {
|
||||
docker network rm "${NETWORK}"
|
||||
docker network rm "${TEST_NETWORK}"
|
||||
}
|
||||
|
||||
@test "checking tls: cipher list - rsa intermediate" {
|
||||
check_ports 'rsa' 'intermediate'
|
||||
function teardown() { _default_teardown ; }
|
||||
|
||||
@test "${TEST_NAME_PREFIX} 'TLS_LEVEL=intermediate' + RSA" {
|
||||
configure_and_run_dms_container 'intermediate' 'rsa'
|
||||
should_support_expected_cipherlists
|
||||
}
|
||||
|
||||
@test "checking tls: cipher list - rsa modern" {
|
||||
check_ports 'rsa' 'modern'
|
||||
@test "${TEST_NAME_PREFIX} 'TLS_LEVEL=intermediate' + ECDSA" {
|
||||
configure_and_run_dms_container 'intermediate' 'ecdsa'
|
||||
should_support_expected_cipherlists
|
||||
}
|
||||
|
||||
@test "checking tls: cipher list - ecdsa intermediate" {
|
||||
check_ports 'ecdsa' 'intermediate'
|
||||
# Only ECDSA with an RSA fallback is tested.
|
||||
# There isn't a situation where RSA with an ECDSA fallback would make sense.
|
||||
@test "${TEST_NAME_PREFIX} 'TLS_LEVEL=intermediate' + ECDSA with RSA fallback" {
|
||||
configure_and_run_dms_container 'intermediate' 'ecdsa' 'rsa'
|
||||
should_support_expected_cipherlists
|
||||
}
|
||||
|
||||
@test "checking tls: cipher list - ecdsa modern" {
|
||||
check_ports 'ecdsa' 'modern'
|
||||
@test "${TEST_NAME_PREFIX} 'TLS_LEVEL=modern' + RSA" {
|
||||
configure_and_run_dms_container 'modern' 'rsa'
|
||||
should_support_expected_cipherlists
|
||||
}
|
||||
|
||||
|
||||
# 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 "${TEST_NAME_PREFIX} 'TLS_LEVEL=modern' + ECDSA" {
|
||||
configure_and_run_dms_container 'modern' 'ecdsa'
|
||||
should_support_expected_cipherlists
|
||||
}
|
||||
|
||||
@test "checking tls: cipher list - ecdsa modern, with rsa fallback" {
|
||||
check_ports 'ecdsa' 'modern' 'rsa'
|
||||
@test "${TEST_NAME_PREFIX} 'TLS_LEVEL=modern' + ECDSA with RSA fallback" {
|
||||
configure_and_run_dms_container 'modern' 'ecdsa' 'rsa'
|
||||
should_support_expected_cipherlists
|
||||
}
|
||||
|
||||
function check_ports() {
|
||||
local KEY_TYPE=$1
|
||||
local TLS_LEVEL=$2
|
||||
function configure_and_run_dms_container() {
|
||||
local TLS_LEVEL=$1
|
||||
local KEY_TYPE=$2
|
||||
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
|
||||
export TEST_VARIANT="${TLS_LEVEL}-${KEY_TYPE}"
|
||||
if [[ -n ${ALT_KEY_TYPE} ]]
|
||||
then
|
||||
KEY_TYPE_LABEL="${KEY_TYPE}_${ALT_KEY_TYPE}"
|
||||
TEST_VARIANT+="-${ALT_KEY_TYPE}"
|
||||
fi
|
||||
local RESULTS_PATH="${KEY_TYPE_LABEL}/${TLS_LEVEL}"
|
||||
|
||||
collect_cipherlist_data
|
||||
export CONTAINER_NAME="${CONTAINER_PREFIX}_${TEST_VARIANT}"
|
||||
# The initial set of args is static across test cases:
|
||||
local CUSTOM_SETUP_ARGUMENTS=(
|
||||
--volume "${TLS_CONFIG_VOLUME}"
|
||||
--network "${TEST_NETWORK}"
|
||||
--network-alias "${TEST_DOMAIN}"
|
||||
--env ENABLE_POP3=1
|
||||
--env SSL_TYPE="manual"
|
||||
)
|
||||
|
||||
# The remaining args are dependent upon test case vars:
|
||||
CUSTOM_SETUP_ARGUMENTS+=(
|
||||
--env TLS_LEVEL="${TLS_LEVEL}"
|
||||
--env SSL_CERT_PATH="/config/ssl/cert.${KEY_TYPE}.pem"
|
||||
--env SSL_KEY_PATH="/config/ssl/key.${KEY_TYPE}.pem"
|
||||
)
|
||||
|
||||
if [[ -n ${ALT_KEY_TYPE} ]]
|
||||
then
|
||||
CUSTOM_SETUP_ARGUMENTS+=(
|
||||
--env SSL_ALT_CERT_PATH="/config/ssl/cert.${ALT_KEY_TYPE}.pem"
|
||||
--env SSL_ALT_KEY_PATH="/config/ssl/key.${ALT_KEY_TYPE}.pem"
|
||||
)
|
||||
fi
|
||||
|
||||
init_with_defaults
|
||||
common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
|
||||
wait_for_smtp_port_in_container "${CONTAINER_NAME}"
|
||||
}
|
||||
|
||||
function should_support_expected_cipherlists() {
|
||||
# Make a directory with test user ownership. Avoids Docker creating this with root ownership.
|
||||
# TODO: Can switch to filename prefix for JSON output when this is resolved: https://github.com/drwetter/testssl.sh/issues/1845
|
||||
local RESULTS_PATH="${TLS_RESULTS_DIR}/${TEST_VARIANT}"
|
||||
mkdir -p "${RESULTS_PATH}"
|
||||
|
||||
collect_cipherlists
|
||||
verify_cipherlists
|
||||
}
|
||||
|
||||
# Verify that the collected results match our expected cipherlists:
|
||||
function verify_cipherlists() {
|
||||
# SMTP: Opportunistic STARTTLS Explicit(25)
|
||||
# Needs to test against cipher lists specific to Port 25 ('_p25' parameter)
|
||||
check_cipherlists "${RESULTS_PATH}/port_25.json" '_p25'
|
||||
|
@ -93,82 +140,56 @@ function check_ports() {
|
|||
check_cipherlists "${RESULTS_PATH}/port_995.json"
|
||||
}
|
||||
|
||||
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}" \
|
||||
--env ENABLE_POP3=1 \
|
||||
--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}" \
|
||||
--hostname "mail.${DOMAIN}" \
|
||||
--tty \
|
||||
"${NAME}" # Image name
|
||||
|
||||
assert_success
|
||||
|
||||
wait_for_tcp_port_in_container 25 tls_test_cipherlists
|
||||
# Using `testssl.sh` we can test each port to collect a list of supported cipher suites (ordered):
|
||||
function collect_cipherlists() {
|
||||
# NOTE: An rDNS query for the container IP will resolve to `<container name>.<network name>.`
|
||||
|
||||
# Make directory with test user ownership. Avoids Docker creating with root ownership.
|
||||
# TODO: Can switch to filename prefix for JSON output when this is resolved: https://github.com/drwetter/testssl.sh/issues/1845
|
||||
mkdir -p "${TLS_RESULTS_DIR}/${RESULTS_PATH}"
|
||||
|
||||
# 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 "/output/port_${PORT}.json" --starttls smtp "${TEST_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.
|
||||
run docker run --rm \
|
||||
--user "$(id -u):$(id -g)" \
|
||||
--network "${NETWORK}" \
|
||||
--network "${TEST_NETWORK}" \
|
||||
--volume "${TLS_CONFIG_VOLUME}" \
|
||||
--volume "${TLS_RESULTS_DIR}/${RESULTS_PATH}/:/output" \
|
||||
--volume "${RESULTS_PATH}:/output" \
|
||||
--workdir "/output" \
|
||||
drwetter/testssl.sh:3.1dev "${TESTSSL_CMD[@]}"
|
||||
|
||||
assert_success
|
||||
}
|
||||
|
||||
# Compares the expected cipher lists against logged test results from `testssl.sh`
|
||||
function check_cipherlists() {
|
||||
local RESULTS_FILEPATH=$1
|
||||
local p25=$2 # optional suffix
|
||||
|
||||
compare_cipherlist "cipherorder_TLSv1_2" "$(get_cipherlist "TLSv1_2${p25}")"
|
||||
compare_cipherlist "cipherorder_TLSv1_3" "$(get_cipherlist 'TLSv1_3')"
|
||||
}
|
||||
|
||||
# Use `jq` to extract a specific cipher list from the target`testssl.sh` results json output file
|
||||
function compare_cipherlist() {
|
||||
local TARGET_CIPHERLIST=$1
|
||||
local RESULTS_FILE=$2
|
||||
local EXPECTED_CIPHERLIST=$3
|
||||
local EXPECTED_CIPHERLIST=$2
|
||||
|
||||
run jq '.scanResult[0].serverPreferences[] | select(.id=="'"${TARGET_CIPHERLIST}"'") | .finding' "${TLS_RESULTS_DIR}/${RESULTS_FILE}"
|
||||
run jq '.scanResult[0].serverPreferences[] | select(.id=="'"${TARGET_CIPHERLIST}"'") | .finding' "${RESULTS_FILEPATH}"
|
||||
assert_success
|
||||
assert_output "${EXPECTED_CIPHERLIST}"
|
||||
}
|
||||
|
||||
# Compares the expected cipher lists against logged test results from `testssl.sh`
|
||||
function check_cipherlists() {
|
||||
local RESULTS_FILE=$1
|
||||
local p25=$2 # optional suffix
|
||||
|
||||
compare_cipherlist "cipherorder_TLSv1_2" "${RESULTS_FILE}" "$(get_cipherlist "TLSv1_2${p25}")"
|
||||
compare_cipherlist "cipherorder_TLSv1_3" "${RESULTS_FILE}" "$(get_cipherlist 'TLSv1_3')"
|
||||
}
|
||||
|
||||
# Expected cipher lists. Should match `TLS_LEVEL` cipher lists set in `scripts/helpers/ssl.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
|
||||
|
@ -185,34 +206,33 @@ function get_cipherlist() {
|
|||
# Associative array for easy querying of required cipher list
|
||||
declare -A CIPHER_LIST
|
||||
|
||||
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 DHE-RSA-AES128-SHA256 DHE-RSA-AES256-SHA256"'
|
||||
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"'
|
||||
# RSA:
|
||||
CIPHER_LIST["intermediate-rsa_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 DHE-RSA-AES128-SHA256 DHE-RSA-AES256-SHA256"'
|
||||
CIPHER_LIST["modern-rsa_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:
|
||||
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-AES256-SHA384"'
|
||||
CIPHER_LIST["ecdsa_modern_TLSv1_2"]='"ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305"'
|
||||
CIPHER_LIST["intermediate-ecdsa_TLSv1_2"]='"ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-AES256-SHA384"'
|
||||
CIPHER_LIST["modern-ecdsa_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_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-RSA-AES256-SHA384 ECDHE-ECDSA-AES256-SHA384 DHE-RSA-AES128-SHA256 DHE-RSA-AES256-SHA256"'
|
||||
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"'
|
||||
CIPHER_LIST["intermediate-ecdsa-rsa_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-RSA-AES256-SHA384 ECDHE-ECDSA-AES256-SHA384 DHE-RSA-AES128-SHA256 DHE-RSA-AES256-SHA256"'
|
||||
CIPHER_LIST["modern-ecdsa-rsa_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_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 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 ARIA128-GCM-SHA256"'
|
||||
# Port 25 is unaffected by `TLS_LEVEL` profiles, 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"]}
|
||||
|
||||
# Port 25 has a different server order, and also includes ARIA, CCM, DHE+CHACHA20-POLY1305 cipher suites:
|
||||
# RSA (Port 25):
|
||||
CIPHER_LIST["intermediate-rsa_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 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 ARIA128-GCM-SHA256"'
|
||||
# ECDSA (Port 25):
|
||||
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-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-CCM8 ECDHE-ECDSA-AES128-CCM ECDHE-ECDSA-ARIA128-GCM-SHA256 ECDHE-ECDSA-AES128-SHA256"'
|
||||
CIPHER_LIST["ecdsa_modern_TLSv1_2_p25"]=${CIPHER_LIST["ecdsa_intermediate_TLSv1_2_p25"]}
|
||||
|
||||
CIPHER_LIST["intermediate-ecdsa_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-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-CCM8 ECDHE-ECDSA-AES128-CCM ECDHE-ECDSA-ARIA128-GCM-SHA256 ECDHE-ECDSA-AES128-SHA256"'
|
||||
# ECDSA + RSA fallback, dual cert support (Port 25):
|
||||
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 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 ARIA128-GCM-SHA256"'
|
||||
CIPHER_LIST["ecdsa_rsa_modern_TLSv1_2_p25"]=${CIPHER_LIST["ecdsa_rsa_intermediate_TLSv1_2_p25"]}
|
||||
CIPHER_LIST["intermediate-ecdsa-rsa_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 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 ARIA128-GCM-SHA256"'
|
||||
|
||||
# Port 25 is unaffected by `TLS_LEVEL` profiles, thus no difference for modern:
|
||||
CIPHER_LIST["modern-rsa_TLSv1_2_p25"]=${CIPHER_LIST["intermediate-rsa_TLSv1_2_p25"]}
|
||||
CIPHER_LIST["modern-ecdsa_TLSv1_2_p25"]=${CIPHER_LIST["intermediate-ecdsa_TLSv1_2_p25"]}
|
||||
CIPHER_LIST["modern-ecdsa-rsa_TLSv1_2_p25"]=${CIPHER_LIST["intermediate-ecdsa-rsa_TLSv1_2_p25"]}
|
||||
|
||||
local TARGET_QUERY="${KEY_TYPE_LABEL}_${TLS_LEVEL}_${TLS_VERSION}"
|
||||
local TARGET_QUERY="${TEST_VARIANT}_${TLS_VERSION}"
|
||||
echo "${CIPHER_LIST[${TARGET_QUERY}]}"
|
||||
fi
|
||||
}
|
||||
|
|
|
@ -1,29 +1,22 @@
|
|||
load "${REPOSITORY_ROOT}/test/test_helper/common"
|
||||
load "${REPOSITORY_ROOT}/test/test_helper/tls"
|
||||
load "${REPOSITORY_ROOT}/test/helper/setup"
|
||||
load "${REPOSITORY_ROOT}/test/helper/common"
|
||||
load "${REPOSITORY_ROOT}/test/helper/tls"
|
||||
|
||||
# Globals referenced from `test_helper/common`:
|
||||
# TEST_NAME TEST_FQDN TEST_TMP_CONFIG
|
||||
TEST_NAME_PREFIX='[Security] TLS (SSL_TYPE=letsencrypt):'
|
||||
CONTAINER1_NAME='dms-test_tls-letsencrypt_default-hostname'
|
||||
CONTAINER2_NAME='dms-test_tls-letsencrypt_fallback-domainname'
|
||||
CONTAINER3_NAME='dms-test_tls-letsencrypt_support-acme-json'
|
||||
export TEST_FQDN='mail.example.test'
|
||||
|
||||
# Requires maintenance (TODO): Yes
|
||||
# Can run tests in parallel?: No
|
||||
function teardown() { _default_teardown ; }
|
||||
|
||||
# Not parallelize friendly when TEST_NAME is static,
|
||||
# presently name of test file: `mail_ssl_letsencrypt`.
|
||||
#
|
||||
# Also shares a common TEST_TMP_CONFIG local folder,
|
||||
# Instead of individual PRIVATE_CONFIG copies.
|
||||
# For this test that is a non-issue, unless run in parallel.
|
||||
|
||||
|
||||
# Applies to all tests:
|
||||
function setup_file() {
|
||||
# Similar to BATS `setup()` method, but invoked manually after
|
||||
# CONTAINER_NAME has been adjusted for the running testcase.
|
||||
function _initial_setup() {
|
||||
init_with_defaults
|
||||
|
||||
# Override default to match the hostname we want to test against instead:
|
||||
export TEST_FQDN='mail.example.test'
|
||||
|
||||
# Prepare certificates in the letsencrypt supported file structure:
|
||||
# Note Certbot uses `privkey.pem`.
|
||||
# NOTE: Certbot uses `privkey.pem`.
|
||||
# `fullchain.pem` is currently what's detected, but we're actually providing the equivalent of `cert.pem` here.
|
||||
# TODO: Verify format/structure is supported for nginx-proxy + acme-companion (uses `acme.sh` to provision).
|
||||
|
||||
|
@ -36,45 +29,38 @@ function setup_file() {
|
|||
_copy_to_letsencrypt_storage 'example.test/with_ca/ecdsa/key.rsa.pem' 'example.test/privkey.pem'
|
||||
}
|
||||
|
||||
# Not used
|
||||
# function teardown_file() {
|
||||
# }
|
||||
|
||||
function teardown() {
|
||||
docker rm -f "${TEST_NAME}"
|
||||
}
|
||||
|
||||
# Should detect and choose the cert for FQDN `mail.example.test` (HOSTNAME):
|
||||
@test "ssl(letsencrypt): Should default to HOSTNAME (mail.example.test)" {
|
||||
local TARGET_DOMAIN='mail.example.test'
|
||||
@test "${TEST_NAME_PREFIX} Should default to HOSTNAME (${TEST_FQDN})" {
|
||||
export CONTAINER_NAME=${CONTAINER1_NAME}
|
||||
_initial_setup
|
||||
|
||||
local TEST_DOCKER_ARGS=(
|
||||
local TARGET_DOMAIN=${TEST_FQDN}
|
||||
local CUSTOM_SETUP_ARGUMENTS=(
|
||||
--volume "${TEST_TMP_CONFIG}/letsencrypt/${TARGET_DOMAIN}/:/etc/letsencrypt/live/${TARGET_DOMAIN}/:ro"
|
||||
--env PERMIT_DOCKER='container'
|
||||
--env SSL_TYPE='letsencrypt'
|
||||
)
|
||||
common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
|
||||
|
||||
common_container_setup 'TEST_DOCKER_ARGS'
|
||||
|
||||
#test hostname has certificate files
|
||||
# Test that certificate files exist for the configured `hostname`:
|
||||
_should_have_valid_config "${TARGET_DOMAIN}" 'privkey.pem' 'fullchain.pem'
|
||||
_should_succesfully_negotiate_tls "${TARGET_DOMAIN}"
|
||||
_should_not_support_fqdn_in_cert 'example.test'
|
||||
}
|
||||
|
||||
|
||||
# Should detect and choose cert for FQDN `example.test` (DOMAINNAME),
|
||||
# as fallback when no cert for FQDN `mail.example.test` (HOSTNAME) exists:
|
||||
@test "ssl(letsencrypt): Should fallback to DOMAINNAME (example.test)" {
|
||||
local TARGET_DOMAIN='example.test'
|
||||
@test "${TEST_NAME_PREFIX} Should fallback to DOMAINNAME (example.test)" {
|
||||
export CONTAINER_NAME=${CONTAINER2_NAME}
|
||||
_initial_setup
|
||||
|
||||
local TEST_DOCKER_ARGS=(
|
||||
local TARGET_DOMAIN='example.test'
|
||||
local CUSTOM_SETUP_ARGUMENTS=(
|
||||
--volume "${TEST_TMP_CONFIG}/letsencrypt/${TARGET_DOMAIN}/:/etc/letsencrypt/live/${TARGET_DOMAIN}/:ro"
|
||||
--env PERMIT_DOCKER='container'
|
||||
--env SSL_TYPE='letsencrypt'
|
||||
)
|
||||
|
||||
common_container_setup 'TEST_DOCKER_ARGS'
|
||||
common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
|
||||
|
||||
#test domain has certificate files
|
||||
_should_have_valid_config "${TARGET_DOMAIN}" 'privkey.pem' 'fullchain.pem'
|
||||
|
@ -87,34 +73,36 @@ function teardown() {
|
|||
#
|
||||
# NOTE: Currently all of the `acme.json` configs have the FQDN match a SAN value,
|
||||
# all Subject CN (`main` in acme.json) are `Smallstep Leaf` which is not an FQDN.
|
||||
# While valid for that field, it does mean there is no test coverage against `main`.
|
||||
@test "ssl(letsencrypt): Traefik 'acme.json' (*.example.test)" {
|
||||
# This test group changes to certs signed with an RSA Root CA key,
|
||||
# These certs all support both FQDNs: `mail.example.test` and `example.test`,
|
||||
# Except for the wildcard cert `*.example.test`, which intentionally excluded `example.test` when created.
|
||||
# We want to maintain the same FQDN (mail.example.test) between the _acme_ecdsa and _acme_rsa tests.
|
||||
local LOCAL_BASE_PATH="${PWD}/test/test-files/ssl/example.test/with_ca/rsa"
|
||||
# While not using a FQDN is valid for that field,
|
||||
# it does mean there is no test coverage against the `acme.json` field `main`.
|
||||
@test "${TEST_NAME_PREFIX} Traefik 'acme.json' (*.example.test)" {
|
||||
export CONTAINER_NAME=${CONTAINER3_NAME}
|
||||
_initial_setup
|
||||
|
||||
# Change default Root CA cert used for verifying chain of trust with openssl:
|
||||
# Override the `_initial_setup()` default Root CA cert (used for verifying the chain of trust via `openssl`):
|
||||
# shellcheck disable=SC2034
|
||||
local TEST_CA_CERT="${TEST_FILES_CONTAINER_PATH}/ssl/example.test/with_ca/rsa/ca-cert.rsa.pem"
|
||||
|
||||
# This test group switches to certs that are signed with an RSA Root CA key instead.
|
||||
# All of these certs support both FQDNs (`mail.example.test` and `example.test`),
|
||||
# Except for the wildcard cert (`*.example.test`), that was created with `example.test` intentionally excluded from SAN.
|
||||
# We want to maintain the same FQDN (`mail.example.test`) between the _acme_ecdsa and _acme_rsa tests.
|
||||
local LOCAL_BASE_PATH="${PWD}/test/test-files/ssl/example.test/with_ca/rsa"
|
||||
|
||||
function _prepare() {
|
||||
# Default `acme.json` for _acme_ecdsa test:
|
||||
cp "${LOCAL_BASE_PATH}/ecdsa.acme.json" "${TEST_TMP_CONFIG}/letsencrypt/acme.json"
|
||||
|
||||
# TODO: Provision wildcard certs via Traefik to inspect if `example.test` non-wildcard is also added to the cert.
|
||||
# shellcheck disable=SC2034
|
||||
local TEST_DOCKER_ARGS=(
|
||||
local CUSTOM_SETUP_ARGUMENTS=(
|
||||
--volume "${TEST_TMP_CONFIG}/letsencrypt/acme.json:/etc/letsencrypt/acme.json:ro"
|
||||
--env LOG_LEVEL='trace'
|
||||
--env PERMIT_DOCKER='container'
|
||||
--env SSL_DOMAIN='*.example.test'
|
||||
--env SSL_TYPE='letsencrypt'
|
||||
)
|
||||
|
||||
common_container_setup 'TEST_DOCKER_ARGS'
|
||||
wait_for_service "${TEST_NAME}" 'changedetector'
|
||||
common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
|
||||
wait_for_service "${CONTAINER_NAME}" 'changedetector'
|
||||
|
||||
# Wait until the changedetector service startup delay is over:
|
||||
repeat_until_success_or_timeout 20 sh -c "$(_get_service_logs 'changedetector') | grep 'Changedetector is ready'"
|
||||
|
@ -184,7 +172,6 @@ function teardown() {
|
|||
# Test Methods
|
||||
#
|
||||
|
||||
|
||||
# Check that Dovecot and Postfix are configured to use a cert for the expected FQDN:
|
||||
function _should_have_valid_config() {
|
||||
local EXPECTED_FQDN=${1}
|
||||
|
@ -199,16 +186,14 @@ function _should_have_valid_config() {
|
|||
|
||||
# CMD ${1} run in container with output checked to match value of ${2}:
|
||||
function _has_matching_line() {
|
||||
run docker exec "${TEST_NAME}" sh -c "${1} | grep '${2}'"
|
||||
_run_in_container bash -c "${1} | grep '${2}'"
|
||||
assert_output "${2}"
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Traefik `acme.json` specific
|
||||
#
|
||||
|
||||
|
||||
# It should log success of extraction for the expected domain and restart Postfix.
|
||||
function _should_have_succeeded_at_extraction() {
|
||||
local EXPECTED_DOMAIN=${1}
|
||||
|
@ -249,7 +234,7 @@ function _should_have_service_reload_count() {
|
|||
local NUM_RELOADS=${1}
|
||||
|
||||
# Count how many times processes (like Postfix and Dovecot) have been reloaded by the `changedetector` service:
|
||||
run docker exec "${TEST_NAME}" sh -c "grep -c 'Completed handling of detected change' /var/log/supervisor/changedetector.log"
|
||||
_run_in_container grep --count 'Completed handling of detected change' '/var/log/supervisor/changedetector.log'
|
||||
assert_output "${NUM_RELOADS}"
|
||||
}
|
||||
|
||||
|
@ -265,12 +250,10 @@ function _should_have_expected_files() {
|
|||
_should_be_equal_in_content "${LE_CERT_PATH}" "${EXPECTED_CERT_PATH}"
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Misc
|
||||
#
|
||||
|
||||
|
||||
# Rename test certificate files to match the expected file structure for letsencrypt:
|
||||
function _copy_to_letsencrypt_storage() {
|
||||
local SRC=${1}
|
||||
|
@ -280,14 +263,18 @@ function _copy_to_letsencrypt_storage() {
|
|||
FQDN_DIR=$(echo "${DEST}" | cut -d '/' -f1)
|
||||
mkdir -p "${TEST_TMP_CONFIG}/letsencrypt/${FQDN_DIR}"
|
||||
|
||||
cp "${PWD}/test/test-files/ssl/${SRC}" "${TEST_TMP_CONFIG}/letsencrypt/${DEST}"
|
||||
if ! cp "${PWD}/test/test-files/ssl/${SRC}" "${TEST_TMP_CONFIG}/letsencrypt/${DEST}"
|
||||
then
|
||||
echo "Could not copy cert file '${SRC}'' to '${DEST}'" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function _should_be_equal_in_content() {
|
||||
local CONTAINER_PATH=${1}
|
||||
local LOCAL_PATH=${2}
|
||||
|
||||
run docker exec "${TEST_NAME}" sh -c "cat ${CONTAINER_PATH}"
|
||||
_run_in_container /bin/bash -c "cat ${CONTAINER_PATH}"
|
||||
assert_output "$(cat "${LOCAL_PATH}")"
|
||||
assert_success
|
||||
}
|
||||
|
@ -295,13 +282,13 @@ function _should_be_equal_in_content() {
|
|||
function _get_service_logs() {
|
||||
local SERVICE=${1:-'mailserver'}
|
||||
|
||||
local CMD_LOGS=(docker exec "${TEST_NAME}" "supervisorctl tail -2200 ${SERVICE}")
|
||||
local CMD_LOGS=(docker exec "${CONTAINER_NAME}" "supervisorctl tail -2200 ${SERVICE}")
|
||||
|
||||
# As the `mailserver` service logs are not stored in a file but output to stdout/stderr,
|
||||
# The `supervisorctl tail` command won't work; we must instead query via `docker logs`:
|
||||
if [[ ${SERVICE} == 'mailserver' ]]
|
||||
then
|
||||
CMD_LOGS=(docker logs "${TEST_NAME}")
|
||||
CMD_LOGS=(docker logs "${CONTAINER_NAME}")
|
||||
fi
|
||||
|
||||
echo "${CMD_LOGS[@]}"
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
#!/usr/bin/env bats
|
||||
load "${REPOSITORY_ROOT}/test/test_helper/common"
|
||||
load "${REPOSITORY_ROOT}/test/helper/setup"
|
||||
load "${REPOSITORY_ROOT}/test/helper/common"
|
||||
|
||||
TEST_NAME_PREFIX='[Security] TLS (SSL_TYPE=manual):'
|
||||
CONTAINER_NAME='dms-test_tls-manual'
|
||||
|
||||
function setup_file() {
|
||||
# Internal copies made by `start-mailserver.sh`:
|
||||
# Internal copies made by `scripts/helpers/ssl.sh`:
|
||||
export PRIMARY_KEY='/etc/dms/tls/key'
|
||||
export PRIMARY_CERT='/etc/dms/tls/cert'
|
||||
export FALLBACK_KEY='/etc/dms/tls/fallback_key'
|
||||
|
@ -14,98 +17,101 @@ function setup_file() {
|
|||
export SSL_ALT_KEY_PATH='/config/ssl/key.rsa.pem'
|
||||
export SSL_ALT_CERT_PATH='/config/ssl/cert.rsa.pem'
|
||||
|
||||
local PRIVATE_CONFIG
|
||||
export DOMAIN_SSL_MANUAL='example.test'
|
||||
PRIVATE_CONFIG=$(duplicate_config_for_container .)
|
||||
export TEST_DOMAIN='example.test'
|
||||
|
||||
docker run -d --name mail_manual_ssl \
|
||||
--volume "${PRIVATE_CONFIG}/:/tmp/docker-mailserver/" \
|
||||
--volume "$(pwd)/test/test-files/ssl/${DOMAIN_SSL_MANUAL}/with_ca/ecdsa/:/config/ssl/:ro" \
|
||||
--env LOG_LEVEL='trace' \
|
||||
--env SSL_TYPE='manual' \
|
||||
--env TLS_LEVEL='modern' \
|
||||
--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_SSL_MANUAL}" \
|
||||
--tty \
|
||||
"${NAME}" # Image name
|
||||
local CUSTOM_SETUP_ARGUMENTS=(
|
||||
--volume "${PWD}/test/test-files/ssl/${TEST_DOMAIN}/with_ca/ecdsa/:/config/ssl/:ro"
|
||||
--env LOG_LEVEL='trace'
|
||||
--env SSL_TYPE='manual'
|
||||
--env TLS_LEVEL='modern'
|
||||
--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}"
|
||||
)
|
||||
|
||||
wait_for_finished_setup_in_container mail_manual_ssl
|
||||
init_with_defaults
|
||||
# Override the default set in `common_container_setup`:
|
||||
export TEST_FQDN="mail.${TEST_DOMAIN}"
|
||||
common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
|
||||
}
|
||||
|
||||
function teardown_file() {
|
||||
docker rm -f mail_manual_ssl
|
||||
function teardown_file() { _default_teardown ; }
|
||||
|
||||
@test "${TEST_NAME_PREFIX} ENV vars provided are valid files" {
|
||||
_run_in_container [ -f "${SSL_CERT_PATH}" ]
|
||||
assert_success
|
||||
|
||||
_run_in_container [ -f "${SSL_KEY_PATH}" ]
|
||||
assert_success
|
||||
|
||||
_run_in_container [ -f "${SSL_ALT_CERT_PATH}" ]
|
||||
assert_success
|
||||
|
||||
_run_in_container [ -f "${SSL_ALT_KEY_PATH}" ]
|
||||
assert_success
|
||||
}
|
||||
|
||||
@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" {
|
||||
@test "${TEST_NAME_PREFIX} manual configuration is correct" {
|
||||
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'
|
||||
_run_in_container 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}"
|
||||
_run_in_container 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}"
|
||||
_run_in_container 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}"
|
||||
_run_in_container 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}"
|
||||
_run_in_container 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 cmp -s "${PRIMARY_KEY}" "${SSL_KEY_PATH}"
|
||||
@test "${TEST_NAME_PREFIX} manual configuration copied files correctly " {
|
||||
_run_in_container cmp -s "${PRIMARY_KEY}" "${SSL_KEY_PATH}"
|
||||
assert_success
|
||||
run docker exec mail_manual_ssl cmp -s "${PRIMARY_CERT}" "${SSL_CERT_PATH}"
|
||||
_run_in_container 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}"
|
||||
_run_in_container cmp -s "${FALLBACK_KEY}" "${SSL_ALT_KEY_PATH}"
|
||||
assert_success
|
||||
run docker exec mail_manual_ssl cmp -s "${FALLBACK_CERT}" "${SSL_ALT_CERT_PATH}"
|
||||
_run_in_container 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
|
||||
@test "${TEST_NAME_PREFIX} manual cert works correctly" {
|
||||
wait_for_tcp_port_in_container 587 "${CONTAINER_NAME}"
|
||||
|
||||
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:')
|
||||
RESULT=$(docker exec "${CONTAINER_NAME}" "${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 docker exec "${CONTAINER_NAME}" [ -f "${CA_CERT}" ]
|
||||
RESULT=$(docker exec "${CONTAINER_NAME}" "${TEST_COMMAND[@]}" -CAfile "${CA_CERT}" | grep 'Verification: OK')
|
||||
assert_equal "${RESULT}" 'Verification: OK'
|
||||
}
|
||||
|
||||
@test "checking ssl: manual cert changes are picked up by check-for-changes" {
|
||||
@test "${TEST_NAME_PREFIX} manual cert changes are picked up by check-for-changes" {
|
||||
printf '%s' 'someThingsChangedHere' \
|
||||
>>"$(pwd)/test/test-files/ssl/${DOMAIN_SSL_MANUAL}/with_ca/ecdsa/key.ecdsa.pem"
|
||||
>>"$(pwd)/test/test-files/ssl/${TEST_DOMAIN}/with_ca/ecdsa/key.ecdsa.pem"
|
||||
|
||||
run timeout 15 docker exec mail_manual_ssl bash -c "tail -F /var/log/supervisor/changedetector.log | sed '/Manual certificates have changed/ q'"
|
||||
run timeout 15 docker exec "${CONTAINER_NAME}" bash -c "tail -F /var/log/supervisor/changedetector.log | sed '/Manual certificates have changed/ q'"
|
||||
assert_success
|
||||
|
||||
sed -i '/someThingsChangedHere/d' "$(pwd)/test/test-files/ssl/${DOMAIN_SSL_MANUAL}/with_ca/ecdsa/key.ecdsa.pem"
|
||||
sed -i '/someThingsChangedHere/d' "$(pwd)/test/test-files/ssl/${TEST_DOMAIN}/with_ca/ecdsa/key.ecdsa.pem"
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue